diff --git a/lib/Target/AVR/AVRISelLowering.cpp b/lib/Target/AVR/AVRISelLowering.cpp index fa8b5d4d3d7b..d188935f3082 100644 --- a/lib/Target/AVR/AVRISelLowering.cpp +++ b/lib/Target/AVR/AVRISelLowering.cpp @@ -1307,32 +1307,37 @@ SDValue AVRTargetLowering::LowerCall(TargetLowering::CallLoweringInfo &CLI, InVals); } -/// Reverse splitted return values to get the "big endian" format required -/// to agree with the calling convention ABI. -static void ReverseArgumentsToBigEndian(MachineFunction &MF, - SmallVector &RVLocs) { - if (RVLocs.size() > 1) { - // Some hackery because SelectionDAGBuilder does not split - // up arguments properly - Type *retType = MF.getFunction().getReturnType(); - if (retType->isStructTy()) { - if (retType->getNumContainedTypes() > 1 && - retType->getNumContainedTypes() > RVLocs.size()) { - for (unsigned i = 0, pos = 0; - i < retType->getNumContainedTypes(); ++i) { - Type *field = retType->getContainedType(i); - if(field->isIntegerTy() && field->getIntegerBitWidth() > 16) { - int Size = field->getIntegerBitWidth() / 16; - std::reverse(RVLocs.begin()+ pos, RVLocs.end() + pos + Size); - pos += Size; - } else { - pos++; - } +/// Reverse splitted return values to "big endian" order +/// so that when registers are assigned from standard CConv +/// we end up with the format required +/// to agree with the avr-gcc calling convention ABI. +/// https://gcc.gnu.org/wiki/avr-gcc +static void ReverseReturnValuePartsToEnsureLittleEndian( + MachineFunction &MF, + SmallVector &RVLocs) { + + // Some hackery because SelectionDAGBuilder does not split + // up arguments properly + Type *retType = MF.getFunction().getReturnType(); + if (retType->isStructTy()) { + // For structs we need more advanced rearrangement. + if (retType->getNumContainedTypes() > 1 && + retType->getNumContainedTypes() > RVLocs.size()) { + for (unsigned i = 0, pos = 0; + i < retType->getNumContainedTypes(); ++i) { + Type *field = retType->getContainedType(i); + if(field->isIntegerTy() && field->getIntegerBitWidth() > 16) { + int Size = field->getIntegerBitWidth() / 16; + std::reverse(RVLocs.begin()+ pos, RVLocs.end() + pos + Size); + pos += Size; + } else { + pos++; } } - } else { - std::reverse(RVLocs.begin(), RVLocs.end()); } + } else { + // For scalar type returns, a simple reversal is fine. + std::reverse(RVLocs.begin(), RVLocs.end()); } } @@ -1353,6 +1358,11 @@ SDValue AVRTargetLowering::LowerCallResult( auto CCFunction = CCAssignFnForReturn(CallConv); CCInfo.AnalyzeCallResult(Ins, CCFunction); + if (CallConv != CallingConv::AVR_BUILTIN && RVLocs.size() > 1) { + ReverseReturnValuePartsToEnsureLittleEndian( + DAG.getMachineFunction(), RVLocs); + } + // Copy all of the result registers out of their specified physreg. for (CCValAssign const &RVLoc : RVLocs) { Chain = DAG.getCopyFromReg(Chain, dl, RVLoc.getLocReg(), RVLoc.getValVT(), @@ -1411,16 +1421,18 @@ AVRTargetLowering::LowerReturn(SDValue Chain, CallingConv::ID CallConv, // If this is the first return lowered for this function, add the regs to // the liveout set for the function. MachineFunction &MF = DAG.getMachineFunction(); - unsigned e = RVLocs.size(); - // Reverse splitted return values to get the "big endian" format required - // to agree with the calling convention ABI. - ReverseArgumentsToBigEndian(MF, RVLocs); + if (RVLocs.size() > 1) { + // Reverse splitted return values to get the "big endian" format required + // to agree with the calling convention ABI. + ReverseReturnValuePartsToEnsureLittleEndian(MF, RVLocs); + } SDValue Flag; SmallVector RetOps(1, Chain); + unsigned numberOfReturnParts = RVLocs.size(); // Copy the result values into the output registers. - for (unsigned i = 0; i != e; ++i) { + for (unsigned i = 0; i != numberOfReturnParts; ++i) { CCValAssign &VA = RVLocs[i]; assert(VA.isRegLoc() && "Can only return in registers!"); diff --git a/test/CodeGen/AVR/call-avr-rust-issue-130-regression.ll b/test/CodeGen/AVR/call-avr-rust-issue-130-regression.ll new file mode 100644 index 000000000000..2c2de5e6359c --- /dev/null +++ b/test/CodeGen/AVR/call-avr-rust-issue-130-regression.ll @@ -0,0 +1,61 @@ +; RUN: llc -O1 < %s -march=avr | FileCheck %s + +; CHECK-LABEL: setServoAngle1 +define hidden i32 @setServoAngle1(i32) local_unnamed_addr { +entry: + %1 = mul i32 %0, 19 +; CHECK: ldi r18, 19 +; CHECK: ldi r19, 0 +; CHECK: ldi r20, 0 +; CHECK: ldi r21, 0 +; CHECK: call __mulsi3 +; CHECK: ret + ret i32 %1 +} + +; CHECK-LABEL: setServoAngle2 +define hidden i16 @setServoAngle2(i32) local_unnamed_addr { +entry: + %1 = mul i32 %0, 19 +; CHECK: ldi r18, 19 +; CHECK: ldi r19, 0 +; CHECK: ldi r20, 0 +; CHECK: ldi r21, 0 +; CHECK: call __mulsi3 + %2 = trunc i32 %1 to i16 +; CHECK: mov r24, r22 +; CHECK: mov r25, r23 +; CHECK: ret + ret i16 %2 +} + +; CHECK-LABEL: setServoAngle3 +define hidden i32 @setServoAngle3(i32) local_unnamed_addr { +entry: + %1 = call i32 @myExternalFunction1(i32 %0, i32 119) +; CHECK: ldi r18, 119 +; CHECK: ldi r19, 0 +; CHECK: ldi r20, 0 +; CHECK: ldi r21, 0 +; CHECK: call myExternalFunction1 +; CHECK: ret + ret i32 %1 +} + +; CHECK-LABEL: setServoAngle4 +define hidden i16 @setServoAngle4(i32) local_unnamed_addr { +entry: + %1 = call i32 @myExternalFunction1(i32 %0, i32 119) +; CHECK: ldi r18, 119 +; CHECK: ldi r19, 0 +; CHECK: ldi r20, 0 +; CHECK: ldi r21, 0 +; CHECK: call myExternalFunction1 + %2 = trunc i32 %1 to i16 +; CHECK: mov r24, r22 +; CHECK: mov r25, r23 +; CHECK: ret + ret i16 %2 +} + +declare i32 @myExternalFunction1(i32, i32) diff --git a/test/CodeGen/AVR/directmem.ll b/test/CodeGen/AVR/directmem.ll index d0674cbc4595..de78dde45584 100644 --- a/test/CodeGen/AVR/directmem.ll +++ b/test/CodeGen/AVR/directmem.ll @@ -84,6 +84,11 @@ define i16 @global16_load() { define void @array16_store() { ; CHECK-LABEL: array16_store: +; CHECK: ldi [[REG1:r[0-9]+]], 221 +; CHECK: ldi [[REG2:r[0-9]+]], 170 +; CHECK: sts int.array+5, [[REG2]] +; CHECK: sts int.array+4, [[REG1]] + ; CHECK: ldi [[REG1:r[0-9]+]], 204 ; CHECK: ldi [[REG2:r[0-9]+]], 170 ; CHECK: sts int.array+3, [[REG2]] @@ -94,11 +99,6 @@ define void @array16_store() { ; CHECK: sts int.array+1, [[REG2]] ; CHECK: sts int.array, [[REG1]] - -; CHECK: ldi [[REG1:r[0-9]+]], 221 -; CHECK: ldi [[REG2:r[0-9]+]], 170 -; CHECK: sts int.array+5, [[REG2]] -; CHECK: sts int.array+4, [[REG1]] store i16 43707, i16* getelementptr inbounds ([3 x i16], [3 x i16]* @int.array, i32 0, i64 0) store i16 43724, i16* getelementptr inbounds ([3 x i16], [3 x i16]* @int.array, i32 0, i64 1) store i16 43741, i16* getelementptr inbounds ([3 x i16], [3 x i16]* @int.array, i32 0, i64 2) @@ -152,6 +152,14 @@ define i32 @global32_load() { define void @array32_store() { ; CHECK-LABEL: array32_store: +; CHECK: ldi [[REG1:r[0-9]+]], 170 +; CHECK: ldi [[REG2:r[0-9]+]], 153 +; CHECK: sts long.array+11, [[REG2]] +; CHECK: sts long.array+10, [[REG1]] +; CHECK: ldi [[REG1:r[0-9]+]], 204 +; CHECK: ldi [[REG2:r[0-9]+]], 187 +; CHECK: sts long.array+9, [[REG2]] +; CHECK: sts long.array+8, [[REG1]] ; CHECK: ldi [[REG1:r[0-9]+]], 102 ; CHECK: ldi [[REG2:r[0-9]+]], 85 ; CHECK: sts long.array+7, [[REG2]] @@ -168,14 +176,6 @@ define void @array32_store() { ; CHECK: ldi [[REG2:r[0-9]+]], 13 ; CHECK: sts long.array+1, [[REG2]] ; CHECK: sts long.array, [[REG1]] -; CHECK: ldi [[REG1:r[0-9]+]], 170 -; CHECK: ldi [[REG2:r[0-9]+]], 153 -; CHECK: sts long.array+11, [[REG2]] -; CHECK: sts long.array+10, [[REG1]] -; CHECK: ldi [[REG1:r[0-9]+]], 204 -; CHECK: ldi [[REG2:r[0-9]+]], 187 -; CHECK: sts long.array+9, [[REG2]] -; CHECK: sts long.array+8, [[REG1]] store i32 2887454020, i32* getelementptr inbounds ([3 x i32], [3 x i32]* @long.array, i32 0, i64 0) store i32 1432778632, i32* getelementptr inbounds ([3 x i32], [3 x i32]* @long.array, i32 0, i64 1) store i32 2578103244, i32* getelementptr inbounds ([3 x i32], [3 x i32]* @long.array, i32 0, i64 2)