diff gen/abi-x86-64.cpp @ 1353:45aca7e7cc88

Remove struct padding when passing or returning in registers on x86-64 (extern(D) only)
author Frits van Bommel <fvbommel wxs.nl>
date Thu, 14 May 2009 20:36:55 +0200
parents 15e9762bb620
children 701d11a1e7b1
line wrap: on
line diff
--- 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();