Add support for emitting GC-ness of the second return register for 16 byte
authorLubomir Litchev <lubol@microsoft.com>
Thu, 25 Feb 2016 05:26:55 +0000 (21:26 -0800)
committerLubomir Litchev <lubol@microsoft.com>
Mon, 29 Feb 2016 23:55:02 +0000 (15:55 -0800)
structs.

This changeset adds support for emitting the GC-ness of the second return
register (RDX for System V Amd64) for multi-register return structs.
It closes a hole in the GC info and resolves multiple GC stress failures.

Fixes dotnet/coreclr#2757.

Commit migrated from https://github.com/dotnet/coreclr/commit/31cb7975335b33bd04268216e3409a4cd6e9dc29

src/coreclr/src/jit/codegen.h
src/coreclr/src/jit/codegencommon.cpp
src/coreclr/src/jit/codegenxarch.cpp
src/coreclr/src/jit/emit.cpp
src/coreclr/src/jit/emit.h
src/coreclr/src/jit/emitxarch.cpp
src/coreclr/src/jit/emitxarch.h
src/coreclr/src/jit/instr.cpp
src/coreclr/src/jit/instr.h
src/coreclr/tests/src/JIT/Methodical/structs/systemvbringup/structrettest.cs

index 574cf1c..a0423e8 100644 (file)
@@ -467,23 +467,25 @@ protected:
 
     void                genPrologPadForReJit();
 
-    void                genEmitCall(int                   callType,
-                                    CORINFO_METHOD_HANDLE methHnd,
-                                    INDEBUG_LDISASM_COMMA(CORINFO_SIG_INFO* sigInfo)
-                                    void*                 addr
-                                    X86_ARG(ssize_t       argSize),
-                                    emitAttr              retSize,
-                                    IL_OFFSETX            ilOffset,
-                                    regNumber             base   = REG_NA,
-                                    bool                  isJump = false,
-                                    bool                  isNoGC = false);
-
+    void                genEmitCall(int                                                 callType,
+                                    CORINFO_METHOD_HANDLE                               methHnd,
+                                    INDEBUG_LDISASM_COMMA(CORINFO_SIG_INFO*             sigInfo)
+                                    void*                                               addr
+                                    X86_ARG(ssize_t                                     argSize),
+                                    emitAttr                                            retSize
+                                    FEATURE_UNIX_AMD64_STRUCT_PASSING_ONLY_ARG(emitAttr secondRetSize),
+                                    IL_OFFSETX                                          ilOffset,
+                                    regNumber                                           base   = REG_NA,
+                                    bool                                                isJump = false,
+                                    bool                                                isNoGC = false);
+    
     void                genEmitCall(int                   callType, 
                                     CORINFO_METHOD_HANDLE methHnd,
                                     INDEBUG_LDISASM_COMMA(CORINFO_SIG_INFO* sigInfo)
                                     GenTreeIndir*         indir
                                     X86_ARG(ssize_t       argSize),
-                                    emitAttr              retSize,
+                                    emitAttr              retSize
+                                    FEATURE_UNIX_AMD64_STRUCT_PASSING_ONLY_ARG(emitAttr secondRetSize),
                                     IL_OFFSETX            ilOffset);
 
 
@@ -937,7 +939,8 @@ public :
 
     void                instEmit_indCall(GenTreePtr     call,
                                          size_t         argSize,
-                                         emitAttr       retSize);
+                                         emitAttr       retSize
+                                         FEATURE_UNIX_AMD64_STRUCT_PASSING_ONLY_ARG(emitAttr    secondRetSize));
 
     void                instEmit_RM     (instruction    ins,
                                          GenTreePtr     tree,
index de2339e..5492a3f 100644 (file)
@@ -9316,15 +9316,17 @@ void                CodeGen::genFnEpilog(BasicBlock* block)
                                  methHnd,
                                  INDEBUG_LDISASM_COMMA(nullptr)
                                  addr,
-                                 0,                     /* argSize */
-                                 EA_UNKNOWN,            /* retSize */
+                                 0,                     // argSize
+                                 EA_UNKNOWN,            // retSize
                                  gcInfo.gcVarPtrSetCur,
                                  gcInfo.gcRegGCrefSetCur,
                                  gcInfo.gcRegByrefSetCur,
-                                 BAD_IL_OFFSET,         /* IL offset*/
-                                 indCallReg,            /* ireg */
-                                 REG_NA, 0, 0,          /* xreg, xmul, disp */
-                                 true);                 /* isJump */
+                                 BAD_IL_OFFSET,         // IL offset
+                                 indCallReg,            // ireg
+                                 REG_NA,                // xreg
+                                 0,                     // xmul
+                                 0,                     // disp
+                                 true);                 // isJump
     }
     else
     {
@@ -9667,8 +9669,9 @@ void                CodeGen::genFnEpilog(BasicBlock* block)
                                        methHnd,
                                        INDEBUG_LDISASM_COMMA(nullptr)
                                        addrInfo.addr,
-                                       0,                         /* argSize */
-                                       EA_UNKNOWN,                /* retSize */
+                                       0,                                                      // argSize
+                                       EA_UNKNOWN                                              // retSize
+                                       FEATURE_UNIX_AMD64_STRUCT_PASSING_ONLY_ARG(EA_UNKNOWN), // secondRetSize
                                        gcInfo.gcVarPtrSetCur,
                                        gcInfo.gcRegGCrefSetCur,
                                        gcInfo.gcRegByrefSetCur,
index 3025675..8b38930 100644 (file)
@@ -5172,16 +5172,17 @@ void CodeGen::genTransferRegGCState(regNumber dst, regNumber src)
 //     pass in 'addr' for a relative call or 'base' for a indirect register call
 //     methHnd - optional, only used for pretty printing 
 //     retSize - emitter type of return for GC purposes, should be EA_BYREF, EA_GCREF, or EA_PTRSIZE(not GC)
-void CodeGen::genEmitCall(int                   callType,
-                          CORINFO_METHOD_HANDLE methHnd,
-                          INDEBUG_LDISASM_COMMA(CORINFO_SIG_INFO* sigInfo)
-                          void*                 addr
-                          X86_ARG(ssize_t       argSize),
-                          emitAttr              retSize,
-                          IL_OFFSETX            ilOffset,
-                          regNumber             base,
-                          bool                  isJump,
-                          bool                  isNoGC)
+void CodeGen::genEmitCall(int                                                   callType,
+                          CORINFO_METHOD_HANDLE                                 methHnd,
+                          INDEBUG_LDISASM_COMMA(CORINFO_SIG_INFO*               sigInfo)
+                          void*                                                 addr
+                          X86_ARG(ssize_t                                       argSize),
+                          emitAttr                                              retSize
+                          FEATURE_UNIX_AMD64_STRUCT_PASSING_ONLY_ARG(emitAttr   secondRetSize),
+                          IL_OFFSETX                                            ilOffset,
+                          regNumber                                             base,
+                          bool                                                  isJump,
+                          bool                                                  isNoGC)
 {
 #if !defined(_TARGET_X86_)
     ssize_t               argSize = 0;
@@ -5191,7 +5192,8 @@ void CodeGen::genEmitCall(int                   callType,
                                INDEBUG_LDISASM_COMMA(sigInfo)
                                addr,
                                argSize,
-                               retSize,
+                               retSize
+                               FEATURE_UNIX_AMD64_STRUCT_PASSING_ONLY_ARG(secondRetSize),
                                gcInfo.gcVarPtrSetCur,
                                gcInfo.gcRegGCrefSetCur,
                                gcInfo.gcRegByrefSetCur,
@@ -5204,13 +5206,14 @@ void CodeGen::genEmitCall(int                   callType,
 // generates an indirect call via addressing mode (call []) given an indir node
 //     methHnd - optional, only used for pretty printing
 //     retSize - emitter type of return for GC purposes, should be EA_BYREF, EA_GCREF, or EA_PTRSIZE(not GC)
-void CodeGen::genEmitCall(int                   callType,
-                          CORINFO_METHOD_HANDLE methHnd,
-                          INDEBUG_LDISASM_COMMA(CORINFO_SIG_INFO* sigInfo)
-                          GenTreeIndir*         indir
-                          X86_ARG(ssize_t       argSize),
-                          emitAttr              retSize,
-                          IL_OFFSETX            ilOffset)
+void CodeGen::genEmitCall(int                                                   callType,
+                          CORINFO_METHOD_HANDLE                                 methHnd,
+                          INDEBUG_LDISASM_COMMA(CORINFO_SIG_INFO*               sigInfo)
+                          GenTreeIndir*                                         indir
+                          X86_ARG(ssize_t                                       argSize),
+                          emitAttr                                              retSize
+                          FEATURE_UNIX_AMD64_STRUCT_PASSING_ONLY_ARG(emitAttr   secondRetSize),
+                          IL_OFFSETX                                            ilOffset)
 {
 #if !defined(_TARGET_X86_)
     ssize_t               argSize = 0;
@@ -5222,7 +5225,8 @@ void CodeGen::genEmitCall(int                   callType,
                                INDEBUG_LDISASM_COMMA(sigInfo)
                                nullptr,
                                argSize,
-                               retSize,
+                               retSize
+                               FEATURE_UNIX_AMD64_STRUCT_PASSING_ONLY_ARG(secondRetSize),
                                gcInfo.gcVarPtrSetCur,
                                gcInfo.gcRegGCrefSetCur,
                                gcInfo.gcRegByrefSetCur,
@@ -5495,7 +5499,6 @@ bool CodeGen::genEmitOptimizedGCWriteBarrier(GCInfo::WriteBarrierForm writeBarri
 void CodeGen::genCallInstruction(GenTreePtr node)
 {
     GenTreeCall *call = node->AsCall();
-
     assert(call->gtOper == GT_CALL);
 
     gtCallTypes callType  = (gtCallTypes)call->gtCallType;
@@ -5529,20 +5532,22 @@ void CodeGen::genCallInstruction(GenTreePtr node)
                 GenTreePtr putArgRegNode = argListPtr->gtOp.gtOp1;
                 assert(putArgRegNode->gtOper == GT_PUTARG_REG);
                 regNumber argReg = REG_NA;
+
                 if (iterationNum == 0)
                 {
                     argReg = curArgTabEntry->regNum;
                 }
-                else if (iterationNum == 1)
-                {
-                    argReg = curArgTabEntry->otherRegNum;
-                }
                 else
                 {
-                    assert(false); // Illegal state.
+                    assert(iterationNum == 1);
+                    argReg = curArgTabEntry->otherRegNum;
                 }
 
                 genConsumeReg(putArgRegNode);
+
+                // Validate the putArgRegNode has the right type.  
+                assert(putArgRegNode->TypeGet() == compiler->GetTypeFromClassificationAndSizes(curArgTabEntry->structDesc.eightByteClassifications[iterationNum],
+                                                                                               curArgTabEntry->structDesc.eightByteSizes[iterationNum]));
                 if (putArgRegNode->gtRegNum != argReg)
                 {
                     inst_RV_RV(ins_Move_Extend(putArgRegNode->TypeGet(), putArgRegNode->InReg()), argReg, putArgRegNode->gtRegNum);
@@ -5668,16 +5673,34 @@ void CodeGen::genCallInstruction(GenTreePtr node)
         genDefineTempLabel(genCreateTempLabel());
     }
 
-    // Determine return value size.
+    // Determine return value size(s).
     emitAttr retSize = EA_PTRSIZE;
-    if (call->gtType == TYP_REF ||
-        call->gtType == TYP_ARRAY)
+
+#ifdef FEATURE_UNIX_AMD64_STRUCT_PASSING
+    emitAttr secondRetSize = EA_UNKNOWN;
+    if (varTypeIsStruct(call->gtType))
     {
-        retSize = EA_GCREF;
+        // Make sure it is a multi-register returned struct,  
+        // otherwise, for a struct passed in a single register   
+        // the call would have a normalized type that is not a struct type.  
+        assert(call->structDesc.passedInRegisters &&
+               (call->structDesc.eightByteCount == CLR_SYSTEMV_MAX_EIGHTBYTES_COUNT_TO_RETURN_IN_REGISTERS));
+
+        retSize = emitTypeSize(compiler->getEightByteType(call->structDesc, 0));
+        secondRetSize = emitTypeSize(compiler->getEightByteType(call->structDesc, 1));
     }
-    else if (call->gtType == TYP_BYREF)
+    else
+#endif // FEATURE_UNIX_AMD64_STRUCT_PASSING  
     {
-        retSize = EA_BYREF;
+        if (call->gtType == TYP_REF ||
+            call->gtType == TYP_ARRAY)
+        {
+            retSize = EA_GCREF;
+        }
+        else if (call->gtType == TYP_BYREF)
+        {
+            retSize = EA_BYREF;
+        }
     }
 
     bool fPossibleSyncHelperCall = false;
@@ -5722,7 +5745,8 @@ void CodeGen::genCallInstruction(GenTreePtr node)
                             INDEBUG_LDISASM_COMMA(sigInfo)
                             (void*) target->AsIndir()->Base()->AsIntConCommon()->IconValue()
                             X86_ARG(argSizeForEmitter),
-                            retSize,
+                            retSize
+                            FEATURE_UNIX_AMD64_STRUCT_PASSING_ONLY_ARG(secondRetSize),
                             ilOffset);
             }
             else
@@ -5734,7 +5758,8 @@ void CodeGen::genCallInstruction(GenTreePtr node)
                             INDEBUG_LDISASM_COMMA(sigInfo)
                             target->AsIndir()
                             X86_ARG(argSizeForEmitter),
-                            retSize,
+                            retSize
+                            FEATURE_UNIX_AMD64_STRUCT_PASSING_ONLY_ARG(secondRetSize),
                             ilOffset);
             }
         }
@@ -5748,7 +5773,8 @@ void CodeGen::genCallInstruction(GenTreePtr node)
                         INDEBUG_LDISASM_COMMA(sigInfo)
                         nullptr //addr
                         X86_ARG(argSizeForEmitter),
-                        retSize,
+                        retSize
+                        FEATURE_UNIX_AMD64_STRUCT_PASSING_ONLY_ARG(secondRetSize),
                         ilOffset,
                         genConsumeReg(target));
         }
@@ -5761,7 +5787,8 @@ void CodeGen::genCallInstruction(GenTreePtr node)
                     INDEBUG_LDISASM_COMMA(sigInfo)
                     (void*) call->gtEntryPoint.addr
                     X86_ARG(argSizeForEmitter),
-                    retSize,
+                    retSize
+                    FEATURE_UNIX_AMD64_STRUCT_PASSING_ONLY_ARG(secondRetSize),
                     ilOffset);
     }
 #endif
@@ -5817,7 +5844,8 @@ void CodeGen::genCallInstruction(GenTreePtr node)
                     INDEBUG_LDISASM_COMMA(sigInfo)
                     addr
                     X86_ARG(argSizeForEmitter),
-                    retSize,
+                    retSize
+                    FEATURE_UNIX_AMD64_STRUCT_PASSING_ONLY_ARG(secondRetSize),
                     ilOffset);
     }
 
@@ -8459,11 +8487,8 @@ CodeGen::genCreateAndStoreGCInfoX64(unsigned codeSize, unsigned prologSize DEBUG
 
 void        CodeGen::genEmitHelperCall(unsigned    helper,
                                        int         argSize,
-                                       emitAttr    retSize
-#ifndef LEGACY_BACKEND
-                                       ,regNumber   callTargetReg /*= REG_NA */
-#endif // !LEGACY_BACKEND
-                                       )
+                                       emitAttr    retSize,
+                                       regNumber   callTargetReg)
 {
     void* addr = nullptr;
     void* pAddr = nullptr;
@@ -8520,19 +8545,20 @@ void        CodeGen::genEmitHelperCall(unsigned    helper,
     }
 
     getEmitter()->emitIns_Call(callType,
-                                compiler->eeFindHelper(helper),
-                                INDEBUG_LDISASM_COMMA(nullptr)
-                                addr,
-                                argSize,
-                                retSize,
-                                gcInfo.gcVarPtrSetCur,
-                                gcInfo.gcRegGCrefSetCur,
-                                gcInfo.gcRegByrefSetCur,
-                                BAD_IL_OFFSET,       /* IL offset */
-                                callTarget,          /* ireg */
-                                REG_NA, 0, 0,        /* xreg, xmul, disp */
-                                false,               /* isJump */
-                                emitter::emitNoGChelper(helper));
+                               compiler->eeFindHelper(helper),
+                               INDEBUG_LDISASM_COMMA(nullptr)
+                               addr,
+                               argSize,
+                               retSize
+                               FEATURE_UNIX_AMD64_STRUCT_PASSING_ONLY_ARG(EA_UNKNOWN), 
+                               gcInfo.gcVarPtrSetCur,
+                               gcInfo.gcRegGCrefSetCur,
+                               gcInfo.gcRegByrefSetCur,
+                               BAD_IL_OFFSET,       // IL offset
+                               callTarget,          // ireg
+                               REG_NA, 0, 0,        // xreg, xmul, disp
+                               false,               // isJump
+                               emitter::emitNoGChelper(helper));
 
     
     regTracker.rsTrashRegSet(killMask);
index 35b16b6..493d13c 100644 (file)
@@ -2953,6 +2953,36 @@ void                emitter::emitDispVarSet()
 
 /*****************************************************************************/
 #endif//DEBUG
+
+#if defined(FEATURE_UNIX_AMD64_STRUCT_PASSING)
+//------------------------------------------------------------------------  
+// emitSetSecondRetRegGCType: Sets the GC type of the second return register for instrDescCGCA struct.  
+//  
+// Arguments:  
+//    id            - The large call instr descriptor to set the second GC return register type on.  
+//    secondRetSize - The EA_SIZE for second return register type.  
+//  
+// Return Value:  
+//    None  
+//  
+
+void            emitter::emitSetSecondRetRegGCType(instrDescCGCA* id, emitAttr secondRetSize)
+{
+    if (EA_IS_GCREF(secondRetSize))
+    {
+        id->idSecondGCref(GCT_GCREF);
+    }
+    else if (EA_IS_BYREF(secondRetSize))
+    {
+        id->idSecondGCref(GCT_BYREF);
+    }
+    else
+    {
+        id->idSecondGCref(GCT_NONE);
+    }
+}
+#endif // defined(FEATURE_UNIX_AMD64_STRUCT_PASSING)
+
 /*****************************************************************************
  *
  *  Allocate an instruction descriptor for an indirect call.
@@ -2964,29 +2994,36 @@ void                emitter::emitDispVarSet()
  *  address mode displacement.
  */
 
-emitter::instrDesc  * emitter::emitNewInstrCallInd(int        argCnt,
-                                                   ssize_t    disp,
-                                                   VARSET_VALARG_TP GCvars,
-                                                   regMaskTP  gcrefRegs,
-                                                   regMaskTP  byrefRegs,
-                                                   emitAttr   retSizeIn)
+emitter::instrDesc  * emitter::emitNewInstrCallInd(int                                                 argCnt,
+                                                   ssize_t                                             disp,
+                                                   VARSET_VALARG_TP                                    GCvars,
+                                                   regMaskTP                                           gcrefRegs,
+                                                   regMaskTP                                           byrefRegs,
+                                                   emitAttr                                            retSizeIn
+                                                   FEATURE_UNIX_AMD64_STRUCT_PASSING_ONLY_ARG(emitAttr secondRetSize))
 {
-    emitAttr  retSize = retSizeIn ? EA_ATTR(retSizeIn) : EA_PTRSIZE;
+    emitAttr       retSize = (retSizeIn != EA_UNKNOWN) ? retSizeIn : EA_PTRSIZE;
 
     bool gcRefRegsInScratch = ((gcrefRegs & RBM_CALLEE_TRASH) != 0);
-    /*
-        Allocate a larger descriptor if any GC values need to be saved
-        or if we have an absurd number of arguments or a large address
-        mode displacement, or we have some byref registers
-     */
-
-    if  (!VarSetOps::IsEmpty(emitComp, GCvars)     ||   // any frame GCvars live
-         (gcRefRegsInScratch)        ||   // any register gc refs live in scratch regs
-         (byrefRegs != 0)            ||   // any register byrefs live
-         (disp < AM_DISP_MIN)        ||   // displacement too negative
-         (disp > AM_DISP_MAX)        ||   // displacement too positive
-         (argCnt > ID_MAX_SMALL_CNS) ||   // too many args
-         (argCnt < 0)                   ) // caller pops arguments
+    
+    // Allocate a larger descriptor if any GC values need to be saved
+    // or if we have an absurd number of arguments or a large address
+    // mode displacement, or we have some byref registers
+    // 
+    // On Amd64 System V OSs a larger descriptor is also needed if the 
+    // call returns a two-register-returned struct and the second 
+    // register (RDX) is a GCRef or ByRef pointer.
+
+
+    if  (!VarSetOps::IsEmpty(emitComp, GCvars)      ||  // any frame GCvars live
+         (gcRefRegsInScratch)                       ||  // any register gc refs live in scratch regs
+         (byrefRegs != 0)                           ||  // any register byrefs live
+         (disp < AM_DISP_MIN)                       ||  // displacement too negative
+         (disp > AM_DISP_MAX)                       ||  // displacement too positive
+         (argCnt > ID_MAX_SMALL_CNS)                ||  // too many args
+         (argCnt < 0)                                   // caller pops arguments
+                                                        // There is a second ref/byref return register.  
+         FEATURE_UNIX_AMD64_STRUCT_PASSING_ONLY(    || EA_IS_GCREF_OR_BYREF(secondRetSize)))
     {
         instrDescCGCA* id;
 
@@ -3000,6 +3037,10 @@ emitter::instrDesc  * emitter::emitNewInstrCallInd(int        argCnt,
         id->idcArgCnt         = argCnt;
         id->idcDisp           = disp;
 
+#ifdef FEATURE_UNIX_AMD64_STRUCT_PASSING
+        emitSetSecondRetRegGCType(id, secondRetSize);
+#endif // FEATURE_UNIX_AMD64_STRUCT_PASSING  
+
         return  id;
     }
     else
@@ -3013,7 +3054,7 @@ emitter::instrDesc  * emitter::emitNewInstrCallInd(int        argCnt,
 
         /* Store the displacement and make sure the value fit */
         id->idAddr()->iiaAddrMode.amDisp  = disp;
- assert(id->idAddr()->iiaAddrMode.amDisp == disp);
       assert(id->idAddr()->iiaAddrMode.amDisp == disp);
 
         /* Save the the live GC registers in the unused 'idReg/idRg2' fields */
         emitEncodeCallGCregs(gcrefRegs, id);
@@ -3033,26 +3074,32 @@ emitter::instrDesc  * emitter::emitNewInstrCallInd(int        argCnt,
  *  and an arbitrarily large argument count.
  */
 
-emitter::instrDesc *emitter::emitNewInstrCallDir(int        argCnt,
-                                                 VARSET_VALARG_TP GCvars,
-                                                 regMaskTP  gcrefRegs,
-                                                 regMaskTP  byrefRegs,
-                                                 emitAttr   retSizeIn)
+emitter::instrDesc *emitter::emitNewInstrCallDir(int                                                 argCnt,
+                                                 VARSET_VALARG_TP                                    GCvars,
+                                                 regMaskTP                                           gcrefRegs,
+                                                 regMaskTP                                           byrefRegs,
+                                                 emitAttr                                            retSizeIn
+                                                 FEATURE_UNIX_AMD64_STRUCT_PASSING_ONLY_ARG(emitAttr secondRetSize))
 {
-    emitAttr       retSize = retSizeIn ? EA_ATTR(retSizeIn) : EA_PTRSIZE;
+    emitAttr       retSize = (retSizeIn != EA_UNKNOWN) ? retSizeIn : EA_PTRSIZE;
+
+    // Allocate a larger descriptor if new GC values need to be saved
+    // or if we have an absurd number of arguments or if we need to
+    // save the scope.
+    // 
+    // On Amd64 System V OSs a larger descriptor is also needed if the 
+    // call returns a two-register-returned struct and the second 
+    // register (RDX) is a GCRef or ByRef pointer.
 
-    /*
-        Allocate a larger descriptor if new GC values need to be saved
-        or if we have an absurd number of arguments or if we need to
-        save the scope.
-     */
     bool gcRefRegsInScratch = ((gcrefRegs & RBM_CALLEE_TRASH) != 0);
 
-    if  (!VarSetOps::IsEmpty(emitComp, GCvars)     ||   // any frame GCvars live
-         gcRefRegsInScratch          ||   // any register gc refs live in scratch regs
-         (byrefRegs != 0)            ||   // any register byrefs live
-         (argCnt > ID_MAX_SMALL_CNS) ||   // too many args
-         (argCnt < 0)                   ) // caller pops arguments
+    if  (!VarSetOps::IsEmpty(emitComp, GCvars)      ||   // any frame GCvars live
+         gcRefRegsInScratch                         ||   // any register gc refs live in scratch regs
+         (byrefRegs != 0)                           ||   // any register byrefs live
+         (argCnt > ID_MAX_SMALL_CNS)                ||   // too many args
+         (argCnt < 0)                                    // caller pops arguments
+                                                         // There is a second ref/byref return register.  
+        FEATURE_UNIX_AMD64_STRUCT_PASSING_ONLY(     || EA_IS_GCREF_OR_BYREF(secondRetSize)))
     {
         instrDescCGCA* id = emitAllocInstrCGCA(retSize);
 
@@ -3066,6 +3113,10 @@ emitter::instrDesc *emitter::emitNewInstrCallDir(int        argCnt,
         id->idcDisp           = 0;  
         id->idcArgCnt         = argCnt;
 
+#ifdef FEATURE_UNIX_AMD64_STRUCT_PASSING
+        emitSetSecondRetRegGCType(id, secondRetSize);
+#endif // FEATURE_UNIX_AMD64_STRUCT_PASSING
+
         return  id;
     }
     else
index df27736..c03e902 100644 (file)
@@ -656,6 +656,13 @@ protected:
 #endif // ARM or x86-LEGACY_BACKEND
 
         // On Amd64, this is where the second DWORD begins
+        // On System V a call could return a struct in 2 registers. The instrDescCGCA struct below has  member that 
+        // stores the GC-ness of the second register.
+        // It is added to the instrDescCGCA and not here (the base struct) since it is not needed by all the instructions.
+        // This struct (instrDesc) is very carefully kept to be no more than 128 bytes. There is no more space to add members
+        // for keeping GC-ness of the second return registers. It will also bloat the base struct unnecessarily
+        // since the GC-ness of the second register is only needed for call instructions.
+        // The instrDescCGCA struct's member keeping the GC-ness of the first return register is _idcSecondRetRegGCType.
         GCtype          _idGCref     :2;  // GCref operand? (value is a "GCtype")
 
         // Note that we use the _idReg1 and _idReg2 fields to hold
@@ -1209,6 +1216,24 @@ protected:
         regMaskTP       idcGcrefRegs;              // ... gcref registers
         regMaskTP       idcByrefRegs;              // ... byref registers
         unsigned        idcArgCnt;                 // ... lots of args or (<0 ==> caller pops args)
+
+#ifdef FEATURE_UNIX_AMD64_STRUCT_PASSING
+        // This method handle the GC-ness of the second register in a 2 register returned struct on System V.
+        GCtype          idSecondGCref() const { return (GCtype)_idcSecondRetRegGCType; }
+        void            idSecondGCref(GCtype gctype) { _idcSecondRetRegGCType = gctype; }
+#endif // FEATURE_UNIX_AMD64_STRUCT_PASSING
+
+    private:
+#ifdef FEATURE_UNIX_AMD64_STRUCT_PASSING
+        // This member stores the GC-ness of the second register in a 2 register returned struct on System V.
+        // It is added to the call struct since it is not needed by the base instrDesc struct, which keeps GC-ness
+        // of the first register for the instCall nodes.
+        // The base instrDesc is very carefully kept to be no more than 128 bytes. There is no more space to add members
+        // for keeping GC-ness of the second return registers. It will also bloat the base struct unnecessarily
+        // since the GC-ness of the second register is only needed for call instructions.
+        // The base struct's member keeping the GC-ness of the first return register is _idGCref.
+        GCtype          _idcSecondRetRegGCType : 2;      // ... GC type for the second return register.  
+#endif // FEATURE_UNIX_AMD64_STRUCT_PASSING  
     };
 
     struct          instrDescArmFP : instrDesc  
@@ -1589,6 +1614,10 @@ private:
 
     regNumber       emitSyncThisObjReg; // where is "this" enregistered for synchronized methods?
 
+#ifdef FEATURE_UNIX_AMD64_STRUCT_PASSING
+    void            emitSetSecondRetRegGCType(instrDescCGCA* id, emitAttr secondRetSize); 
+#endif // FEATURE_UNIX_AMD64_STRUCT_PASSING
+
     static void     emitEncodeCallGCregs(regMaskTP regs, instrDesc *id);
     static unsigned emitDecodeCallGCregs(instrDesc *id);
 
index 4bf0aff..c1c086f 100644 (file)
@@ -5316,20 +5316,21 @@ void                emitter::emitIns_J(instruction   ins,
 
 void                emitter::emitIns_Call(EmitCallType  callType,
                                           CORINFO_METHOD_HANDLE methHnd,
-                                          INDEBUG_LDISASM_COMMA(CORINFO_SIG_INFO* sigInfo)     // used to report call sites to the EE
-                                          void*         addr,
-                                          ssize_t       argSize,
-                                          emitAttr      retSize,
-                                          VARSET_VALARG_TP ptrVars,
-                                          regMaskTP     gcrefRegs,
-                                          regMaskTP     byrefRegs,
-                                          IL_OFFSETX    ilOffset /* = BAD_IL_OFFSET */,
-                                          regNumber     ireg    /* = REG_NA */,
-                                          regNumber     xreg    /* = REG_NA */,
-                                          unsigned      xmul    /* = 0     */,
-                                          ssize_t       disp    /* = 0     */,
-                                          bool          isJump  /* = false */,
-                                          bool          isNoGC  /* = false */)
+                                          INDEBUG_LDISASM_COMMA(CORINFO_SIG_INFO*   sigInfo)  // used to report call sites to the EE
+                                          void*                                     addr,
+                                          ssize_t                                   argSize,
+                                          emitAttr                                  retSize
+                                          FEATURE_UNIX_AMD64_STRUCT_PASSING_ONLY_ARG(emitAttr    secondRetSize),
+                                          VARSET_VALARG_TP                          ptrVars,
+                                          regMaskTP                                 gcrefRegs,
+                                          regMaskTP                                 byrefRegs,
+                                          IL_OFFSETX                                ilOffset, // = BAD_IL_OFFSET
+                                          regNumber                                 ireg,     // = REG_NA
+                                          regNumber                                 xreg,     // = REG_NA
+                                          unsigned                                  xmul,     // = 0
+                                          ssize_t                                   disp,     // = 0
+                                          bool                                      isJump,   // = false
+                                          bool                                      isNoGC)   // = false
 {
     /* Sanity check the arguments depending on callType */
 
@@ -5513,17 +5514,29 @@ void                emitter::emitIns_Call(EmitCallType  callType,
                callType == EC_INDIR_SR     || callType == EC_INDIR_C ||
                callType == EC_INDIR_ARD);
 
-        id  = emitNewInstrCallInd(argCnt, disp, ptrVars, gcrefRegs, byrefRegs, retSize);
+        id  = emitNewInstrCallInd(argCnt,
+                                  disp,
+                                  ptrVars,
+                                  gcrefRegs,
+                                  byrefRegs,
+                                  retSize
+                                  FEATURE_UNIX_AMD64_STRUCT_PASSING_ONLY_ARG(secondRetSize));
     }
     else
     {
-        /* Helper/static/nonvirtual/function calls (direct or through handle),
-           and calls to an absolute addr. */
+        // Helper/static/nonvirtual/function calls (direct or through handle),
+        // and calls to an absolute addr.
 
-        assert(callType == EC_FUNC_TOKEN || callType == EC_FUNC_TOKEN_INDIR ||
+        assert(callType == EC_FUNC_TOKEN ||
+               callType == EC_FUNC_TOKEN_INDIR ||
                callType == EC_FUNC_ADDR);
 
-        id  = emitNewInstrCallDir(argCnt, ptrVars, gcrefRegs, byrefRegs, retSize);
+        id  = emitNewInstrCallDir(argCnt,
+                                  ptrVars,
+                                  gcrefRegs,
+                                  byrefRegs,
+                                  retSize
+                                  FEATURE_UNIX_AMD64_STRUCT_PASSING_ONLY_ARG(secondRetSize));
     }
 
     /* Update the emitter's live GC ref sets */
@@ -10532,9 +10545,29 @@ size_t              emitter::emitOutputInstr(insGroup* ig, instrDesc* id, BYTE**
 
         // If the method returns a GC ref, mark EAX appropriately
         if (id->idGCref() == GCT_GCREF)
+        {
             gcrefRegs |= RBM_EAX;
-        else if  (id->idGCref() == GCT_BYREF)
+        }
+        else if (id->idGCref() == GCT_BYREF)
+        {
             byrefRegs |= RBM_EAX;
+        }
+        
+#ifdef FEATURE_UNIX_AMD64_STRUCT_PASSING
+        // If is a multi-register return method is called, mark RDX appropriately (for System V AMD64).  
+        if (id->idIsLargeCall())
+        {
+            instrDescCGCA* idCall = (instrDescCGCA*)id;
+            if (idCall->idSecondGCref() == GCT_GCREF)
+            {
+                gcrefRegs |= RBM_RDX;
+            }
+            else if (idCall->idSecondGCref() == GCT_BYREF)
+            {
+                byrefRegs |= RBM_RDX;
+            }
+        }
+#endif // FEATURE_UNIX_AMD64_STRUCT_PASSING  
 
         // If the GC register set has changed, report the new set
         if (gcrefRegs != emitThisGCrefRegs)
index 1f8ead4..594d0d0 100644 (file)
@@ -163,18 +163,20 @@ private:
     instrDesc      *emitNewInstrAmd     (emitAttr attr, ssize_t dsp);
     instrDesc      *emitNewInstrAmdCns  (emitAttr attr, ssize_t dsp, int cns);
 
-    instrDesc      *emitNewInstrCallDir (int        argCnt,
-                                         VARSET_VALARG_TP GCvars,
-                                         regMaskTP  gcrefRegs,
-                                         regMaskTP  byrefRegs,
-                                         emitAttr   retSize);
-
-    instrDesc      *emitNewInstrCallInd( int        argCnt,
-                                         ssize_t    disp,
-                                         VARSET_VALARG_TP GCvars,
-                                         regMaskTP  gcrefRegs,
-                                         regMaskTP  byrefRegs,
-                                         emitAttr   retSize);
+    instrDesc      *emitNewInstrCallDir (int                                    argCnt,
+                                         VARSET_VALARG_TP                       GCvars,
+                                         regMaskTP                              gcrefRegs,
+                                         regMaskTP                              byrefRegs,
+                                         emitAttr                               retSize
+                                         FEATURE_UNIX_AMD64_STRUCT_PASSING_ONLY_ARG(emitAttr secondRegSize));
+
+    instrDesc      *emitNewInstrCallInd( int                                    argCnt,
+                                         ssize_t                                disp,
+                                         VARSET_VALARG_TP                       GCvars,
+                                         regMaskTP                              gcrefRegs,
+                                         regMaskTP                              byrefRegs,
+                                         emitAttr                               retSize
+                                         FEATURE_UNIX_AMD64_STRUCT_PASSING_ONLY_ARG(emitAttr secondRegSize));
 
     void            emitGetInsCns   (instrDesc *id, CnsVal *cv);
     ssize_t         emitGetInsAmdCns(instrDesc *id, CnsVal *cv);
@@ -463,35 +465,37 @@ public:
         EC_COUNT
     };
 
-    void            emitIns_Call   (EmitCallType    callType,
-                                    CORINFO_METHOD_HANDLE methHnd,
-                                    CORINFO_SIG_INFO* sigInfo,     // used to report call sites to the EE
-                                    void*           addr,
-                                    ssize_t         argSize,
-                                    emitAttr        retSize,
-                                    VARSET_VALARG_TP ptrVars,
-                                    regMaskTP       gcrefRegs,
-                                    regMaskTP       byrefRegs,
-                                    GenTreeIndir *  indir,
-                                    bool            isJump = false,
-                                    bool            isNoGC = false);
-
-    void            emitIns_Call   (EmitCallType    callType,
-                                    CORINFO_METHOD_HANDLE methHnd,
+    void            emitIns_Call   (EmitCallType                            callType,
+                                    CORINFO_METHOD_HANDLE                   methHnd,
+                                    CORINFO_SIG_INFO*                       sigInfo,     // used to report call sites to the EE
+                                    void*                                   addr,
+                                    ssize_t                                 argSize,
+                                    emitAttr                                retSize
+                                    FEATURE_UNIX_AMD64_STRUCT_PASSING_ONLY_ARG(emitAttr  secondRegSize),
+                                    VARSET_VALARG_TP                        ptrVars,
+                                    regMaskTP                               gcrefRegs,
+                                    regMaskTP                               byrefRegs,
+                                    GenTreeIndir *                          indir,
+                                    bool                                    isJump = false,
+                                    bool                                    isNoGC = false);
+
+    void            emitIns_Call   (EmitCallType                            callType,
+                                    CORINFO_METHOD_HANDLE                   methHnd,
                                     INDEBUG_LDISASM_COMMA(CORINFO_SIG_INFO* sigInfo)     // used to report call sites to the EE
-                                    void*           addr,
-                                    ssize_t         argSize,
-                                    emitAttr        retSize,
-                                    VARSET_VALARG_TP ptrVars,
-                                    regMaskTP       gcrefRegs,
-                                    regMaskTP       byrefRegs,
-                                    IL_OFFSETX      ilOffset = BAD_IL_OFFSET,
-                                    regNumber       ireg = REG_NA,
-                                    regNumber       xreg = REG_NA,
-                                    unsigned        xmul = 0,
-                                    ssize_t         disp = 0,
-                                    bool            isJump = false,
-                                    bool            isNoGC = false);
+                                    void*                                   addr,
+                                    ssize_t                                 argSize,
+                                    emitAttr                                retSize
+                                    FEATURE_UNIX_AMD64_STRUCT_PASSING_ONLY_ARG(emitAttr  secondRegSize),
+                                    VARSET_VALARG_TP                        ptrVars,
+                                    regMaskTP                               gcrefRegs,
+                                    regMaskTP                               byrefRegs,
+                                    IL_OFFSETX                              ilOffset = BAD_IL_OFFSET,
+                                    regNumber                               ireg = REG_NA,
+                                    regNumber                               xreg = REG_NA,
+                                    unsigned                                xmul = 0,
+                                    ssize_t                                 disp = 0,
+                                    bool                                    isJump = false,
+                                    bool                                    isNoGC = false);
 
 #ifdef _TARGET_AMD64_
     // Is the last instruction emitted a call instruction?
index c6ca9d3..1bd1ab6 100644 (file)
@@ -1205,9 +1205,10 @@ void                CodeGen::sched_AM(instruction  ins,
  *  Emit a "call [r/m]" instruction (the r/m operand given by a tree).
  */
 
-void                CodeGen::instEmit_indCall(GenTreePtr   call,
-                                              size_t       argSize,
-                                              emitAttr     retSize)
+void                CodeGen::instEmit_indCall(GenTreePtr                                call,
+                                              size_t                                    argSize,
+                                              emitAttr                                  retSize
+                                              FEATURE_UNIX_AMD64_STRUCT_PASSING_ONLY_ARG(emitAttr    secondRetSize))
 {
     GenTreePtr              addr;
 
@@ -1244,15 +1245,16 @@ void                CodeGen::instEmit_indCall(GenTreePtr   call,
         {
             ssize_t     funcPtr = addr->gtIntCon.gtIconVal;
 
-            getEmitter()->emitIns_Call( emitter::EC_FUNC_ADDR,
-                                      NULL,    // methHnd
-                                      INDEBUG_LDISASM_COMMA(sigInfo)
-                                      (void*) funcPtr,
-                                      argSize,
-                                      retSize,
-                                      gcInfo.gcVarPtrSetCur,
-                                      gcInfo.gcRegGCrefSetCur,
-                                      gcInfo.gcRegByrefSetCur);
+            getEmitter()->emitIns_Call(emitter::EC_FUNC_ADDR,
+                                       NULL,    // methHnd
+                                       INDEBUG_LDISASM_COMMA(sigInfo)
+                                       (void*) funcPtr,
+                                       argSize,
+                                       retSize
+                                       FEATURE_UNIX_AMD64_STRUCT_PASSING_ONLY_ARG(secondRetSize),
+                                       gcInfo.gcVarPtrSetCur,
+                                       gcInfo.gcRegGCrefSetCur,
+                                       gcInfo.gcRegByrefSetCur);
             return;
         }
     }
@@ -1305,15 +1307,16 @@ void                CodeGen::instEmit_indCall(GenTreePtr   call,
             {
                 ssize_t     funcPtr = addr->gtIntCon.gtIconVal;
 
-                getEmitter()->emitIns_Call( emitter::EC_FUNC_ADDR,
-                                          NULL,    // methHnd
-                                          INDEBUG_LDISASM_COMMA(sigInfo)
-                                          (void*) funcPtr,
-                                          argSize,
-                                          retSize,
-                                          gcInfo.gcVarPtrSetCur,
-                                          gcInfo.gcRegGCrefSetCur,
-                                          gcInfo.gcRegByrefSetCur);
+                getEmitter()->emitIns_Call(emitter::EC_FUNC_ADDR,
+                                           NULL,    // methHnd
+                                           INDEBUG_LDISASM_COMMA(sigInfo)
+                                           (void*) funcPtr,
+                                           argSize,
+                                           retSize
+                                           FEATURE_UNIX_AMD64_STRUCT_PASSING_ONLY_ARG(secondRetSize),
+                                           gcInfo.gcVarPtrSetCur,
+                                           gcInfo.gcRegGCrefSetCur,
+                                           gcInfo.gcRegByrefSetCur);
                 return;
             }
         }
@@ -1369,17 +1372,21 @@ void                CodeGen::instEmit_indCall(GenTreePtr   call,
 
 #endif // CPU_LOAD_STORE_ARCH
 
-    getEmitter()->emitIns_Call( emitCallType,
-                              NULL,   // methHnd
-                              INDEBUG_LDISASM_COMMA(sigInfo)
-                              NULL,                 // addr
-                              argSize,
-                              retSize,
-                              gcInfo.gcVarPtrSetCur,
-                              gcInfo.gcRegGCrefSetCur,
-                              gcInfo.gcRegByrefSetCur,
-                              BAD_IL_OFFSET,        // ilOffset
-                              brg, xrg, mul, cns);  // addressing mode values
+    getEmitter()->emitIns_Call(emitCallType,
+                               NULL,   // methHnd
+                               INDEBUG_LDISASM_COMMA(sigInfo)
+                               NULL,                 // addr
+                               argSize,
+                               retSize
+                               FEATURE_UNIX_AMD64_STRUCT_PASSING_ONLY_ARG(secondRetSize),
+                               gcInfo.gcVarPtrSetCur,
+                               gcInfo.gcRegGCrefSetCur,
+                               gcInfo.gcRegByrefSetCur,
+                               BAD_IL_OFFSET,        // ilOffset
+                               brg,
+                               xrg,
+                               mul,
+                               cns);  // addressing mode values
 }
 
 #ifdef LEGACY_BACKEND
index c1e34af..ff73dd4 100644 (file)
@@ -259,23 +259,24 @@ DECLARE_TYPED_ENUM(emitAttr,unsigned)
 }
 END_DECLARE_TYPED_ENUM(emitAttr,unsigned)
 
-# define EA_ATTR(x)          ((emitAttr) (x))
-# define EA_SIZE(x)          ((emitAttr) ( ((unsigned) (x)) &  EA_SIZE_MASK)      )
-# define EA_SIZE_IN_BYTES(x) ((UNATIVE_OFFSET)   (EA_SIZE(x)))
-# define EA_SET_SIZE(x,sz)   ((emitAttr) ((((unsigned) (x)) & ~EA_SIZE_MASK) | sz))
-# define EA_SET_FLG(x,flg)   ((emitAttr) ( ((unsigned) (x)) |  flg  )      )
-# define EA_4BYTE_DSP_RELOC  (EA_SET_FLG(EA_4BYTE,EA_DSP_RELOC_FLG)        )
-# define EA_PTR_DSP_RELOC    (EA_SET_FLG(EA_PTRSIZE,EA_DSP_RELOC_FLG)      )
-# define EA_HANDLE_CNS_RELOC (EA_SET_FLG(EA_PTRSIZE,EA_CNS_RELOC_FLG)      )
-# define EA_IS_OFFSET(x)     ((((unsigned) (x)) & ((unsigned) EA_OFFSET_FLG)) != 0)
-# define EA_IS_GCREF(x)      ((((unsigned) (x)) & ((unsigned) EA_GCREF_FLG )) != 0)
-# define EA_IS_BYREF(x)      ((((unsigned) (x)) & ((unsigned) EA_BYREF_FLG )) != 0)
-# define EA_IS_DSP_RELOC(x)  ((((unsigned) (x)) & ((unsigned) EA_DSP_RELOC_FLG )) != 0)
-# define EA_IS_CNS_RELOC(x)  ((((unsigned) (x)) & ((unsigned) EA_CNS_RELOC_FLG )) != 0)
-# define EA_IS_RELOC(x)      (EA_IS_DSP_RELOC(x) || EA_IS_CNS_RELOC(x))
-# define EA_TYPE(x)          ((emitAttr) ( ((unsigned) (x)) & ~(EA_OFFSET_FLG | EA_DSP_RELOC_FLG | EA_CNS_RELOC_FLG) ) )
-
-#define EmitSize(x) (EA_ATTR(genTypeSize(TypeGet(x))))
+#define EA_ATTR(x)                  ((emitAttr)(x))
+#define EA_SIZE(x)                  ((emitAttr)(((unsigned)(x)) &  EA_SIZE_MASK))
+#define EA_SIZE_IN_BYTES(x)         ((UNATIVE_OFFSET)(EA_SIZE(x)))
+#define EA_SET_SIZE(x, sz)          ((emitAttr)((((unsigned)(x)) & ~EA_SIZE_MASK) | sz))
+#define EA_SET_FLG(x, flg)          ((emitAttr)(((unsigned)(x)) | flg))
+#define EA_4BYTE_DSP_RELOC          (EA_SET_FLG(EA_4BYTE, EA_DSP_RELOC_FLG))
+#define EA_PTR_DSP_RELOC            (EA_SET_FLG(EA_PTRSIZE, EA_DSP_RELOC_FLG))
+#define EA_HANDLE_CNS_RELOC         (EA_SET_FLG(EA_PTRSIZE, EA_CNS_RELOC_FLG))
+#define EA_IS_OFFSET(x)             ((((unsigned)(x)) & ((unsigned)EA_OFFSET_FLG)) != 0)
+#define EA_IS_GCREF(x)              ((((unsigned)(x)) & ((unsigned)EA_GCREF_FLG)) != 0)
+#define EA_IS_BYREF(x)              ((((unsigned)(x)) & ((unsigned)EA_BYREF_FLG)) != 0)
+#define EA_IS_GCREF_OR_BYREF(x)     ((((unsigned)(x)) & ((unsigned)(EA_BYREF_FLG | EA_GCREF_FLG))) != 0)
+#define EA_IS_DSP_RELOC(x)          ((((unsigned)(x)) & ((unsigned)EA_DSP_RELOC_FLG)) != 0)
+#define EA_IS_CNS_RELOC(x)          ((((unsigned)(x)) & ((unsigned)EA_CNS_RELOC_FLG)) != 0)
+#define EA_IS_RELOC(x)              (EA_IS_DSP_RELOC(x) || EA_IS_CNS_RELOC(x))
+#define EA_TYPE(x)                  ((emitAttr)(((unsigned)(x)) & ~(EA_OFFSET_FLG | EA_DSP_RELOC_FLG | EA_CNS_RELOC_FLG)))
+
+#define EmitSize(x)                 (EA_ATTR(genTypeSize(TypeGet(x))))
 
 // Enum specifying the instruction set for generating floating point or SIMD code.
 enum InstructionSet
index a5ebce5..d8a74d7 100644 (file)
@@ -38,12 +38,18 @@ namespace structinreg
         public float f1;
     }
 
+    struct Test35
+    {  
+        public Foo3 foo1;  
+        public Foo3 foo2;  
+    }
 
     class Program0
     {
         [MethodImplAttribute(MethodImplOptions.NoInlining)]
         Test30 test1()
         {
+            Console.WriteLine("From Program0:test1!");
             Test30 test1 = default(Test30);
             test1.i1 = 1;
             test1.i2 = 2;
@@ -54,6 +60,7 @@ namespace structinreg
         [MethodImplAttribute(MethodImplOptions.NoInlining)]
         Test31 test2()
         {
+            Console.WriteLine("From Program0:test2!");
             Test31 test2 = default(Test31);
             Foo3 foo = new Foo3();
             foo.iFoo = 3;
@@ -64,6 +71,7 @@ namespace structinreg
         [MethodImplAttribute(MethodImplOptions.NoInlining)]
         Test32 test3()
         {
+            Console.WriteLine("From Program0:test3!");
             Test32 test3 = default(Test32);
             Foo3 foo = new Foo3();
             foo.iFoo = 4;
@@ -75,6 +83,7 @@ namespace structinreg
         [MethodImplAttribute(MethodImplOptions.NoInlining)]
         Test33 test4()
         {
+            Console.WriteLine("From Program0:test4!");
             Test33 test4 = default(Test33);
             Foo3 foo1 = new Foo3();
             Foo3 foo2 = new Foo3();
@@ -104,6 +113,24 @@ namespace structinreg
         }
 
         [MethodImplAttribute(MethodImplOptions.NoInlining)]
+        Test35 test6(Test35 t5)
+        {  
+            int fRes = t5.foo1.iFoo + t5.foo2.iFoo;  
+            Console.WriteLine("From Test6 members: {0} {1}", t5.foo1.iFoo, t5.foo2.iFoo);  
+            Console.WriteLine("From Test6: Res {0}", fRes);  
+            if (fRes != 43) {  
+                throw new Exception("Failed inside test6 test!");  
+            }  
+  
+            Test35 tst5 = default(Test35);  
+            tst5.foo1 = new Foo3();  
+            tst5.foo2 = new Foo3();  
+            tst5.foo1.iFoo = 28;  
+            tst5.foo2.iFoo = 29;  
+            return tst5;  
+        }  
+        [MethodImplAttribute(MethodImplOptions.NoInlining)]
         public static int Main1()
         {
             Program0 p = new Program0();
@@ -149,6 +176,19 @@ namespace structinreg
                 throw new Exception("Failed test5 test!");
             }
 
+            Test35 test6 = default(Test35);
+            test6.foo1 = new Foo3();
+            test6.foo2 = new Foo3();
+            test6.foo1.iFoo = 21;
+            test6.foo2.iFoo = 22;
+
+            Test35 t6Res = p.test6(test6);
+
+            Console.WriteLine("test6 Result: {0}", t6Res.foo1.iFoo + t6Res.foo2.iFoo);
+            if ((t6Res.foo1.iFoo + t6Res.foo2.iFoo) != 57) {
+                throw new Exception("Failed test6 test!");
+            }
+
             return 100;
         }
     }