public override Reg CGenValue(Env env, CGenState state) { // GCC's IA-32 calling convention // Caller is responsible to push all arguments to the stack in reverse order. // Each argument is at least aligned to 4 bytes - even a char would take 4 bytes. // The return value is stored in %eax, or %st(0), if it is a scalar. // // The stack would look like this after pushing all the arguments: // +--------+ // | .... | // +--------+ // | argn | // +--------+ // | .... | // +--------+ // | arg2 | // +--------+ // | arg1 | // +--------+ <- %esp before call // // Things are different with structs and unions. // Since structs may not fit in 4 bytes, it has to be returned in memory. // Caller allocates a chunk of memory for the struct and push the address of it as an extra argument. // Callee returns %eax with that address. // // The stack would look like this after pushing all the arguments: // +--------+ // +--> | struct | <- struct should be returned here. // | +--------+ // | | argn | // | +--------+ // | | .... | // | +--------+ // | | arg2 | // | +--------+ // | | arg1 | // | +--------+ // +----| addr | <- %esp before call // +--------+ // state.NEWLINE(); state.COMMENT($"Before pushing the arguments, stack size = {state.StackSize}."); var r_pack = Utils.PackArguments(args.Select(_ => _.type).ToList()); Int32 pack_size = r_pack.Item1; IReadOnlyList<Int32> offsets = r_pack.Item2; if (type is TStructOrUnion) { // If the function returns a struct // Allocate space for return value. state.COMMENT("Allocate space for returning stack."); state.CGenExpandStackWithAlignment(type.SizeOf, type.Alignment); // Temporarily store the address in %eax. state.MOVL(Reg.ESP, Reg.EAX); // add an extra argument and move all other arguments upwards. pack_size += ExprType.SIZEOF_POINTER; offsets = offsets.Select(_ => _ + ExprType.SIZEOF_POINTER).ToList(); } // Allocate space for arguments. // If returning struct, the extra pointer is included. state.COMMENT($"Arguments take {pack_size} bytes."); state.CGenExpandStackBy(pack_size); state.NEWLINE(); // Store the address as the first argument. if (type is TStructOrUnion) { state.COMMENT("Putting extra argument for struct return address."); state.MOVL(Reg.EAX, 0, Reg.ESP); state.NEWLINE(); } // This is the stack size before calling the function. Int32 header_base = -state.StackSize; // Push the arguments onto the stack in reverse order for (Int32 i = args.Count; i-- > 0;) { Expr arg = args[i]; Int32 pos = header_base + offsets[i]; state.COMMENT($"Argument {i} is at {pos}"); Reg ret = arg.CGenValue(env, state); switch (arg.type.kind) { case ExprType.Kind.ARRAY: case ExprType.Kind.CHAR: case ExprType.Kind.UCHAR: case ExprType.Kind.SHORT: case ExprType.Kind.USHORT: case ExprType.Kind.LONG: case ExprType.Kind.ULONG: case ExprType.Kind.POINTER: if (ret != Reg.EAX) { throw new InvalidProgramException(); } state.MOVL(Reg.EAX, pos, Reg.EBP); break; case ExprType.Kind.DOUBLE: if (ret != Reg.ST0) { throw new InvalidProgramException(); } state.FSTPL(pos, Reg.EBP); break; case ExprType.Kind.FLOAT: if (ret != Reg.ST0) { throw new InvalidProgramException(); } state.FSTPL(pos, Reg.EBP); break; case ExprType.Kind.STRUCT_OR_UNION: if (ret != Reg.EAX) { throw new InvalidProgramException(); } state.MOVL(Reg.EAX, Reg.ESI); state.LEA(pos, Reg.EBP, Reg.EDI); state.MOVL(arg.type.SizeOf, Reg.ECX); state.CGenMemCpy(); break; default: throw new InvalidProgramException(); } state.NEWLINE(); } // When evaluating arguments, the stack might be changed. // We must restore the stack. state.CGenForceStackSizeTo(-header_base); // Get function address if (func.type is TFunction) { func.CGenAddress(env, state); } else if (func.type is TPointer) { func.CGenValue(env, state); } else { throw new InvalidProgramException(); } state.CALL("*%eax"); state.COMMENT("Function returned."); state.NEWLINE(); if (type.kind == ExprType.Kind.FLOAT || type.kind == ExprType.Kind.DOUBLE) { return Reg.ST0; } else { return Reg.EAX; } }
public override Reg CGenValue(CGenState state) { // GCC's IA-32 calling convention // Caller is responsible to push all arguments to the stack in reverse order. // Each argument is at least aligned to 4 bytes - even a char would take 4 bytes. // The return Value is stored in %eax, or %st(0), if it is a scalar. // // The stack would look like this after pushing all the arguments: // +--------+ // | .... | // +--------+ // | argn | // +--------+ // | .... | // +--------+ // | arg2 | // +--------+ // | arg1 | // +--------+ <- %esp before call // // Things are different with structs and unions. // Since structs may not fit in 4 bytes, it has to be returned in memory. // Caller allocates a chunk of memory for the struct and push the address of it as an extra argument. // Callee returns %eax with that address. // // The stack would look like this after pushing all the arguments: // +--------+ // +--> | struct | <- struct should be returned here. // | +--------+ // | | argn | // | +--------+ // | | .... | // | +--------+ // | | arg2 | // | +--------+ // | | arg1 | // | +--------+ // +----| addr | <- %esp before call // +--------+ // state.NEWLINE(); state.COMMENT($"Before pushing the arguments, stack size = {state.StackSize}."); var r_pack = Utils.PackArguments(this.Args.Select(_ => _.Type).ToList()); Int32 pack_size = r_pack.Item1; IReadOnlyList <Int32> offsets = r_pack.Item2; if (this.Type is StructOrUnionType) { // If the function returns a struct // Allocate space for return Value. state.COMMENT("Allocate space for returning stack."); state.CGenExpandStackWithAlignment(this.Type.SizeOf, this.Type.Alignment); // Temporarily store the address in %eax. state.MOVL(Reg.ESP, Reg.EAX); // add an extra argument and move all other arguments upwards. pack_size += ExprType.SIZEOF_POINTER; offsets = offsets.Select(_ => _ + ExprType.SIZEOF_POINTER).ToList(); } // Allocate space for arguments. // If returning struct, the extra pointer is included. state.COMMENT($"Arguments take {pack_size} bytes."); state.CGenExpandStackBy(pack_size); state.NEWLINE(); // Store the address as the first argument. if (this.Type is StructOrUnionType) { state.COMMENT("Putting extra argument for struct return address."); state.MOVL(Reg.EAX, 0, Reg.ESP); state.NEWLINE(); } // This is the stack size before calling the function. Int32 header_base = -state.StackSize; // Push the arguments onto the stack in reverse order for (Int32 i = this.Args.Count; i-- > 0;) { Expr arg = this.Args[i]; Int32 pos = header_base + offsets[i]; state.COMMENT($"Argument {i} is at {pos}"); Reg ret = arg.CGenValue(state); switch (arg.Type.Kind) { case ExprTypeKind.ARRAY: case ExprTypeKind.CHAR: case ExprTypeKind.UCHAR: case ExprTypeKind.SHORT: case ExprTypeKind.USHORT: case ExprTypeKind.LONG: case ExprTypeKind.ULONG: case ExprTypeKind.POINTER: if (ret != Reg.EAX) { throw new InvalidProgramException(); } state.MOVL(Reg.EAX, pos, Reg.EBP); break; case ExprTypeKind.DOUBLE: if (ret != Reg.ST0) { throw new InvalidProgramException(); } state.FSTPL(pos, Reg.EBP); break; case ExprTypeKind.FLOAT: if (ret != Reg.ST0) { throw new InvalidProgramException(); } state.FSTPL(pos, Reg.EBP); break; case ExprTypeKind.STRUCT_OR_UNION: if (ret != Reg.EAX) { throw new InvalidProgramException(); } state.MOVL(Reg.EAX, Reg.ESI); state.LEA(pos, Reg.EBP, Reg.EDI); state.MOVL(arg.Type.SizeOf, Reg.ECX); state.CGenMemCpy(); break; default: throw new InvalidProgramException(); } state.NEWLINE(); } // When evaluating arguments, the stack might be changed. // We must restore the stack. state.CGenForceStackSizeTo(-header_base); // Get function address if (this.Func.Type is FunctionType) { this.Func.CGenAddress(state); } else if (this.Func.Type is PointerType) { this.Func.CGenValue(state); } else { throw new InvalidProgramException(); } state.CALL("*%eax"); state.COMMENT("Function returned."); state.NEWLINE(); if (this.Type.Kind == ExprTypeKind.FLOAT || this.Type.Kind == ExprTypeKind.DOUBLE) { return(Reg.ST0); } return(Reg.EAX); }