diff --git a/src/main/java/de/hsrm/compiler/Klang/visitors/GenASM.java b/src/main/java/de/hsrm/compiler/Klang/visitors/GenASM.java index df059c4..2987d20 100644 --- a/src/main/java/de/hsrm/compiler/Klang/visitors/GenASM.java +++ b/src/main/java/de/hsrm/compiler/Klang/visitors/GenASM.java @@ -71,48 +71,12 @@ public class GenASM implements Visitor { private int currentFunctionStartLabel = 0; private Parameter[] currentFunctionParams; - private boolean prepareRegisters(Expression lhs, Expression rhs) { - boolean lhsIsFloat = lhs.type.equals(Type.getFloatType()); - boolean rhsIsFloat = rhs.type.equals(Type.getFloatType()); - if (lhsIsFloat && rhsIsFloat) { - lhs.welcome(this); - asm.mov("sd", "%xmm0", "%xmm2"); - rhs.welcome(this); - asm.mov("sd", "%xmm2", "%xmm0"); - asm.mov("sd", "%xmm2", "%xmm0"); - return true; - } else if (lhsIsFloat && !rhsIsFloat) { - lhs.welcome(this); - rhs.welcome(this); - asm.cvtsi2sd("%rax", "%xmm1"); - return true; - } else if (!lhsIsFloat && rhsIsFloat) { - lhs.welcome(this); - asm.cvtsi2sd("%rax", "%xmm2"); - rhs.welcome(this); - asm.mov("sd", "%xmm0", "%xmm1"); - asm.mov("sd", "%xmm2", "%xmm0"); - return true; - } else { - lhs.welcome(this); - asm.push("q", "%rax"); - rhs.welcome(this); - asm.mov("q", "%rax", "%rbx"); - asm.pop("q", "%rax"); - return false; - } - } - public GenASM(String mainName, Map structs) { this.mainName = mainName; this.structs = structs; this.asm = new ASM(); } - public GenASM(Map structs) { - this("main", structs); - } - public String toAsm() { return asm.toAsm(); } @@ -573,32 +537,32 @@ public class GenASM implements Visitor { e.name = "main_by_user"; } - int lblStart = ++lCount; - this.currentFunctionStartLabel = lblStart; - this.currentFunctionParams = e.parameters; + var lblStart = ++lCount; + currentFunctionStartLabel = lblStart; + currentFunctionParams = e.parameters; asm.functionHead(e.name); asm.push("q", "%rbp"); asm.mov("q", "%rsp", "%rbp"); asm.label(lblStart); - // hole die anzahl der lokalen variablen - this.vars = new TreeSet(); - HashMap types = new HashMap(); - GetVars getvars = new GetVars(vars, types); - getvars.visit(e); + // Get the number of local variables. + // TODO: Do this during context analysis and save the result as an attribute of FunctionDefinition. + vars = new TreeSet<>(); + new GetVars(vars, new HashMap<>()).visit(e); - // Erzeuge ein environment - this.env = new HashMap(); + // Create a new environment + env = new HashMap<>(); - // Merke dir die offsets der parameter, die direkt auf den stack gelegt wurden - int offset = 16; // Per Stack übergebene Parameter liegen über unserem BSP - int ri = 0; - int fi = 0; - // Per stack übergebene variablen in env registrieren + // Remember the offsets of the arguments that were placed on the stack. + var offset = 16; // Variables that were passed via the stack are located above our BSP + var ri = 0; + var fi = 0; + + // Add arguments that were passed via the stack to the environment var registerParameters = new ArrayList(); for (int i = 0; i < e.parameters.length; i++) { if (e.parameters[i].type.equals(Type.getFloatType())) { - if (fi >= this.floatRegisters.length) { + if (fi >= floatRegisters.length) { // parameter is on stack already env.put(e.parameters[i].name, offset); offset += 8; @@ -608,7 +572,7 @@ public class GenASM implements Visitor { fi++; } } else { - if (ri >= this.registers.length) { + if (ri >= registers.length) { // parameter is on stack already env.put(e.parameters[i].name, offset); offset += 8; @@ -623,26 +587,42 @@ public class GenASM implements Visitor { offset = 0; ri = 0; fi = 0; + + // Push arguments that were passed via registers onto the stack + // and add them to the environment. for (var param: registerParameters) { if (param.type.equals(Type.getFloatType())) { - asm.mov("q", this.floatRegisters[fi], "%rax"); + asm.mov("q", floatRegisters[fi], "%rax"); asm.push("q", "%rax"); offset -= 8; - this.env.put(param.name, offset); // negative, liegt unter aktuellem BP + env.put(param.name, offset); // negative, liegt unter aktuellem BP fi++; } else { - asm.push("q", this.registers[ri]); + asm.push("q", registers[ri]); offset -= 8; - this.env.put(param.name, offset); // negative, liegt unter aktuellem BP + env.put(param.name, offset); // negative, liegt unter aktuellem BP ri++; } } - // Reserviere Platz auf dem Stack für jede lokale variable - for (String lok_var : vars) { - offset -= 8; - asm.push("q", 0); - this.env.put(lok_var, offset); + // Reserve memory on the stack for the local variables. + if (!vars.isEmpty()) { + // Each variable is at most 8 bytes in size. + asm.sub("q", "$" + (8 * vars.size()), "%rsp"); + + // Save the offsets (they are relative to rbp) + for (String lok_var : vars) { + offset -= 8; + this.env.put(lok_var, offset); + } + } + + // Check the stack alignment and correct if necessary + if ((registerParameters.size() + vars.size()) % 2 != 0) { + // Since each variable is 8 bytes and the stack is 16 byte aligned + // we need to add 8 bytes to the stack to make it aligned + // if there is an odd number of parameters and local variables. + asm.sub("q", "$8", "%rsp"); } e.block.welcome(this); @@ -851,12 +831,25 @@ public class GenASM implements Visitor { asm.push("q", "%rax"); } + // Make sure the stack is aligned before calling malloc + if (e.args.length % 2 != 0) { + // an odd number of arguments means we called pushq an odd number of times + // which results in an unaligned stack. Subtract 8 from the stack pointer to + // re-align the stack + asm.sub("q", "$8", "%rsp"); + } + // allocate heap memory by calling malloc var structDef = this.structs.get(e.structName); asm.mov("l", Helper.getFieldSizeBytes(structDef), "%edi"); asm.call("malloc@PLT"); - // push args into struct memory, last arg is ontop of the stack + // Get rid of the stack alignment if there was any + if (e.args.length % 2 != 0) { + asm.add("q", "$8", "%rsp"); + } + + // push args into struct memory, last arg is on top of the stack for (int i = e.args.length - 1; i >= 0; i--) { asm.pop("q", Helper.getFieldOffset(structDef, i) + "(%rax)"); } @@ -910,4 +903,35 @@ public class GenASM implements Visitor { return null; } + private boolean prepareRegisters(Expression lhs, Expression rhs) { + boolean lhsIsFloat = lhs.type.equals(Type.getFloatType()); + boolean rhsIsFloat = rhs.type.equals(Type.getFloatType()); + if (lhsIsFloat && rhsIsFloat) { + lhs.welcome(this); + asm.mov("sd", "%xmm0", "%xmm2"); + rhs.welcome(this); + asm.mov("sd", "%xmm2", "%xmm0"); + asm.mov("sd", "%xmm2", "%xmm0"); + return true; + } else if (lhsIsFloat && !rhsIsFloat) { + lhs.welcome(this); + rhs.welcome(this); + asm.cvtsi2sd("%rax", "%xmm1"); + return true; + } else if (!lhsIsFloat && rhsIsFloat) { + lhs.welcome(this); + asm.cvtsi2sd("%rax", "%xmm2"); + rhs.welcome(this); + asm.mov("sd", "%xmm0", "%xmm1"); + asm.mov("sd", "%xmm2", "%xmm0"); + return true; + } else { + lhs.welcome(this); + asm.push("q", "%rax"); + rhs.welcome(this); + asm.mov("q", "%rax", "%rbx"); + asm.pop("q", "%rax"); + return false; + } + } } \ No newline at end of file diff --git a/src/test/struct/struct.c b/src/test/struct/struct.c index ca512fe..fa0c4bb 100644 --- a/src/test/struct/struct.c +++ b/src/test/struct/struct.c @@ -94,6 +94,11 @@ int testStructCreation() { struct_testExpected_l("init inner field a", 20, resultRec->b->a); struct_testExpected_s("init inner field b", NULL, resultRec->b->b); + // The result of this test is always 1 because if the tests fail + // a segmentation fault occurs. + long alignmentResult = isStackAlignedBeforeFunctionCall(); + struct_testExpected_l("stack alignment before malloc", 1, alignmentResult); + free(resultRec); free(innerStruct); } diff --git a/src/test/struct/struct.h b/src/test/struct/struct.h index 459e30c..ac67098 100644 --- a/src/test/struct/struct.h +++ b/src/test/struct/struct.h @@ -28,4 +28,6 @@ long getStructFieldRecA(struct testStructRec *t); struct testStructRec *getStructFieldRecB(struct testStructRec *t); struct testStructRec *setStructFieldRecA(struct testStructRec *t, long a); -struct testStructRec *setStructFieldRecB(struct testStructRec *t, struct testStructRec *b); \ No newline at end of file +struct testStructRec *setStructFieldRecB(struct testStructRec *t, struct testStructRec *b); + +long isStackAlignedBeforeFunctionCall(); \ No newline at end of file diff --git a/src/test/test.k b/src/test/test.k index 5f24c1d..6e9e416 100644 --- a/src/test/test.k +++ b/src/test/test.k @@ -555,6 +555,72 @@ function getTestStructRec(a: int, b: testStructRec): testStructRec { return create testStructRec(a, b); } +struct evenFieldsStruct { + a: int; + b: int; +} + +struct oddFieldsStruct { + a: int; + b: int; + c: int; +} + +function stackAlignmentTestInternal1(): oddFieldsStruct { + // an odd amount of constructor parameters could lead to an unaligned stack + // which results in a segmentation fault when calling malloc + return create oddFieldsStruct(1, 1, 1); +} + +function stackAlignmentTestInternal2(): evenFieldsStruct { + // an odd amount of local variables could lead to an unaligned stack + // which results in a segmentation fault when calling malloc + let a: evenFieldsStruct = create evenFieldsStruct(1, 1); + return a; +} + +function stackAlignmentTestInternal3(a: int): evenFieldsStruct { + // an odd amount of function parameters could lead to an unaligned stack + // which results in a segmentation fault when calling malloc + return create evenFieldsStruct(1, 1); +} + +function stackAlignmentTestInternal4(a: int): evenFieldsStruct { + // if (function parameters + local variables + constructor parameters) is odd + // then this could lead to an unaligned stack + // which results in a segmentation faul when calling malloc + let b: int = 0; + let c: int = 0; + return create evenFieldsStruct(1, 1); +} + +function stackAlignmentTestInternal5(a: int, b: int): evenFieldsStruct { + // if (function parameters + local variables + constructor parameters) is odd + // then this could lead to an unaligned stack + // which results in a segmentation faul when calling malloc + let c: int = 0; + return create evenFieldsStruct(1, 1); +} + +function stackAlignmentTestInternal6(a: int, b: int): oddFieldsStruct { + // if (function parameters + local variables + constructor parameters) is odd + // then this could lead to an unaligned stack + // which results in a segmentation faul when calling malloc + let c: int = 0; + let d: int = 0; + return create oddFieldsStruct(1, 1, 1); +} + +function isStackAlignedBeforeFunctionCall(): int { + let a: oddFieldsStruct = stackAlignmentTestInternal1(); + let b: evenFieldsStruct = stackAlignmentTestInternal2(); + let c: evenFieldsStruct = stackAlignmentTestInternal3(1); + let d: evenFieldsStruct = stackAlignmentTestInternal4(1); + let e: evenFieldsStruct = stackAlignmentTestInternal5(1, 1); + let f: oddFieldsStruct = stackAlignmentTestInternal6(1, 1); + return 1; +} + function getStructFieldA(t: testStruct): int { return t.a; }