# HG changeset patch # User Frits van Bommel # Date 1242326215 -7200 # Node ID 45aca7e7cc88388c2e3faaa103560aad54bd9c38 # Parent b9f5f7c5db7374ea5c795cdfa6ccfa8241506e9d Remove struct padding when passing or returning in registers on x86-64 (extern(D) only) diff -r b9f5f7c5db73 -r 45aca7e7cc88 gen/abi-x86-64.cpp --- a/gen/abi-x86-64.cpp Thu May 14 17:23:55 2009 +0200 +++ b/gen/abi-x86-64.cpp Thu May 14 20:36:55 2009 +0200 @@ -43,6 +43,7 @@ #include "gen/llvmhelpers.h" #include "gen/abi.h" #include "gen/abi-x86-64.h" +#include "gen/structs.h" //#include "gen/llvm-version.h" // only use is commented out. #include "ir/irfunction.h" @@ -498,6 +499,38 @@ }; +/// Removes padding fields for (non-union-containing!) structs +struct RemoveStructPadding : ABIRewrite { + /// get a rewritten value back to its original form + virtual LLValue* get(Type* dty, DValue* v) { + LLValue* lval = DtoAlloca(dty, ".rewritetmp"); + + // Make sure the padding is zero, so struct comparisons work. + // TODO: Only do this if there's padding, and/or only initialize padding. + DtoMemSetZero(lval, DtoConstSize_t(getTypePaddedSize(DtoType(dty)))); + + DtoPaddedStruct(dty, v->getRVal(), lval); + return lval; + } + + /// get a rewritten value back to its original form and store result in provided lvalue + /// this one is optional and defaults to calling the one above + virtual void getL(Type* dty, DValue* v, llvm::Value* lval) { + DtoPaddedStruct(dty, v->getRVal(), lval); + } + + /// put out rewritten value + virtual LLValue* put(Type* dty, DValue* v) { + return DtoUnpaddedStruct(dty, v->getRVal()); + } + + /// return the transformed type for this rewrite + virtual const LLType* type(Type* dty, const LLType* t) { + return DtoUnpaddedStructType(dty); + } +}; + + struct RegCount { unsigned char int_regs, sse_regs; }; @@ -505,6 +538,7 @@ struct X86_64TargetABI : TargetABI { X86_64_C_struct_rewrite struct_rewrite; + RemoveStructPadding remove_padding; void newFunctionType(TypeFunction* tf) { funcTypeStack.push_back(FuncTypeData(tf->linkage)); @@ -544,6 +578,7 @@ return funcTypeStack.back().state; } + void fixup_D(IrFuncTyArg& arg); void fixup(IrFuncTyArg& arg); }; @@ -644,6 +679,19 @@ } // Helper function for rewriteFunctionType. +// Structs passed or returned in registers are passed here +// to get their padding removed (if necessary). +void X86_64TargetABI::fixup_D(IrFuncTyArg& arg) { + assert(arg.type->ty == Tstruct); + LLType* abiTy = DtoUnpaddedStructType(arg.type); + + if (abiTy && abiTy != arg.ltype) { + arg.ltype = abiTy; + arg.rewrite = &remove_padding; + } +} + +// Helper function for rewriteFunctionType. // Return type and parameters are passed here (unless they're already in memory) // to get the rewrite applied (if necessary). void X86_64TargetABI::fixup(IrFuncTyArg& arg) { @@ -657,13 +705,41 @@ } void X86_64TargetABI::rewriteFunctionType(TypeFunction* tf) { - // extern(D) is handled entirely by passByVal and returnInArg + IrFuncTy& fty = tf->fty; - if (tf->linkage != LINKd) { + if (tf->linkage == LINKd) { + if (!fty.arg_sret) { + Type* rt = fty.ret->type->toBasetype(); + if (rt->ty == Tstruct) { + Logger::println("x86-64 D ABI: Transforming return type"); + fixup_D(*fty.ret); + } + } + + Logger::println("x86-64 D ABI: Transforming arguments"); + LOG_SCOPE; + + for (IrFuncTy::ArgIter I = fty.args.begin(), E = fty.args.end(); I != E; ++I) { + IrFuncTyArg& arg = **I; + + if (Logger::enabled()) + Logger::cout() << "Arg: " << arg.type->toChars() << '\n'; + + // Arguments that are in memory are of no interest to us. + if (arg.byref) + continue; + + Type* ty = arg.type->toBasetype(); + if (ty->ty == Tstruct) + fixup_D(arg); + + if (Logger::enabled()) + Logger::cout() << "New arg type: " << *arg.ltype << '\n'; + } + + } else { // TODO: See if this is correct for more than just extern(C). - IrFuncTy& fty = tf->fty; - if (!fty.arg_sret) { Logger::println("x86-64 ABI: Transforming return type"); Type* rt = fty.ret->type->toBasetype(); diff -r b9f5f7c5db73 -r 45aca7e7cc88 gen/structs.cpp --- a/gen/structs.cpp Thu May 14 17:23:55 2009 +0200 +++ b/gen/structs.cpp Thu May 14 20:36:55 2009 +0200 @@ -1,6 +1,8 @@ #include #include "gen/llvm.h" +#include "llvm/AbstractTypeUser.h" +#include "llvm/ADT/DenseMap.h" #include "mtype.h" #include "aggregate.h" @@ -332,3 +334,80 @@ return values; } +/// Return the type returned by DtoUnpaddedStruct called on a value of the +/// specified type. +/// Union types will get expanded into a struct, with a type for each member. +LLType* DtoUnpaddedStructType(Type* dty) { + assert(dty->ty == Tstruct); + + typedef llvm::DenseMap CacheT; + static CacheT cache; + CacheT::iterator it = cache.find(dty); + if (it != cache.end()) + return it->second; + + TypeStruct* sty = (TypeStruct*) dty; + Array& fields = sty->sym->fields; + + std::vector types; + + for (unsigned i = 0; i < fields.dim; i++) { + VarDeclaration* vd = (VarDeclaration*) fields.data[i]; + const LLType* fty; + if (vd->type->ty == Tstruct) { + // Nested structs are the only members that can contain padding + fty = DtoUnpaddedStructType(vd->type); + } else { + fty = DtoType(vd->type); + } + types.push_back(fty); + } + LLType* Ty = LLStructType::get(types); + cache.insert(std::make_pair(dty, Ty)); + return Ty; +} + +/// Return the struct value represented by v without the padding fields. +/// Unions will be expanded, with a value for each member. +/// Note: v must be a pointer to a struct, but the return value will be a +/// first-class struct value. +LLValue* DtoUnpaddedStruct(Type* dty, LLValue* v) { + assert(dty->ty == Tstruct); + TypeStruct* sty = (TypeStruct*) dty; + Array& fields = sty->sym->fields; + + LLValue* newval = llvm::UndefValue::get(DtoUnpaddedStructType(dty)); + + for (unsigned i = 0; i < fields.dim; i++) { + VarDeclaration* vd = (VarDeclaration*) fields.data[i]; + LLValue* fieldptr = DtoIndexStruct(v, sty->sym, vd); + LLValue* fieldval; + if (vd->type->ty == Tstruct) { + // Nested structs are the only members that can contain padding + fieldval = DtoUnpaddedStruct(vd->type, fieldptr); + } else { + fieldval = DtoLoad(fieldptr); + } + newval = DtoInsertValue(newval, fieldval, i); + } + return newval; +} + +/// Undo the transformation performed by DtoUnpaddedStruct, writing to lval. +void DtoPaddedStruct(Type* dty, LLValue* v, LLValue* lval) { + assert(dty->ty == Tstruct); + TypeStruct* sty = (TypeStruct*) dty; + Array& fields = sty->sym->fields; + + for (unsigned i = 0; i < fields.dim; i++) { + VarDeclaration* vd = (VarDeclaration*) fields.data[i]; + LLValue* fieldptr = DtoIndexStruct(lval, sty->sym, vd); + LLValue* fieldval = DtoExtractValue(v, i); + if (vd->type->ty == Tstruct) { + // Nested structs are the only members that can contain padding + DtoPaddedStruct(vd->type, fieldval, fieldptr); + } else { + DtoStore(fieldval, fieldptr); + } + } +} diff -r b9f5f7c5db73 -r 45aca7e7cc88 gen/structs.h --- a/gen/structs.h Thu May 14 17:23:55 2009 +0200 +++ b/gen/structs.h Thu May 14 20:36:55 2009 +0200 @@ -18,4 +18,18 @@ /// index a struct one level LLValue* DtoIndexStruct(LLValue* src, StructDeclaration* sd, VarDeclaration* vd); +/// Return the type returned by DtoUnpaddedStruct called on a value of the +/// specified type. +/// Union types will get expanded into a struct, with a type for each member. +LLType* DtoUnpaddedStructType(Type* dty); + +/// Return the struct value represented by v without the padding fields. +/// Unions will be expanded, with a value for each member. +/// Note: v must be a pointer to a struct, but the return value will be a +/// first-class struct value. +LLValue* DtoUnpaddedStruct(Type* dty, LLValue* v); + +/// Undo the transformation performed by DtoUnpaddedStruct, writing to lval. +void DtoPaddedStruct(Type* dty, LLValue* v, LLValue* lval); + #endif diff -r b9f5f7c5db73 -r 45aca7e7cc88 gen/tollvm.cpp --- a/gen/tollvm.cpp Thu May 14 17:23:55 2009 +0200 +++ b/gen/tollvm.cpp Thu May 14 20:36:55 2009 +0200 @@ -630,6 +630,18 @@ ////////////////////////////////////////////////////////////////////////////////////////// +LLValue* DtoInsertValue(LLValue* aggr, LLValue* v, unsigned idx) +{ + return gIR->ir->CreateInsertValue(aggr, v, idx); +} + +LLValue* DtoExtractValue(LLValue* aggr, unsigned idx) +{ + return gIR->ir->CreateExtractValue(aggr, idx); +} + +////////////////////////////////////////////////////////////////////////////////////////// + const LLPointerType* isaPointer(LLValue* v) { return llvm::dyn_cast(v->getType()); diff -r b9f5f7c5db73 -r 45aca7e7cc88 gen/tollvm.h --- a/gen/tollvm.h Thu May 14 17:23:55 2009 +0200 +++ b/gen/tollvm.h Thu May 14 20:36:55 2009 +0200 @@ -68,6 +68,8 @@ void DtoAlignedStore(LLValue* src, LLValue* dst); LLValue* DtoBitCast(LLValue* v, const LLType* t, const char* name=0); LLConstant* DtoBitCast(LLConstant* v, const LLType* t); +LLValue* DtoInsertValue(LLValue* aggr, LLValue* v, unsigned idx); +LLValue* DtoExtractValue(LLValue* aggr, unsigned idx); // llvm::dyn_cast wrappers const LLPointerType* isaPointer(LLValue* v);