Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
(cherry picked from commit f1630e5)
  • Loading branch information
TimNN authored and Lazarus535 committed Oct 2, 2019
1 parent ba813f7 commit dfd4102
Show file tree
Hide file tree
Showing 3 changed files with 174 additions and 9 deletions.
30 changes: 30 additions & 0 deletions llvm/docs/AVRTarget.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
=================================
Documentation for the AVR backend
=================================

.. contents::
:local:

Calling convention
==================

External links
--------------

* The avr-gcc .. _wiki: https://gcc.gnu.org/wiki/avr-gcc#Calling_Convention has a description of the C calling convention they use

* The avr-libc .. documentation: http://www.nongnu.org/avr-libc/user-manual/FAQ.html#faq_reg_usage has a small summary of the C calling convention


Function return values
----------------------

The avr-gcc documentation states that

Return values with a size of 1 byte up to and including a size of 8 bytes will be returned in registers. Return values whose size is outside that range will be returned in memory.

-- avr-gcc wiki, October 2018

This does not strictly seem to be the case. Scalar return values like i8, i16, and i64 do indeed seem to follow this rule, according to the output of avr-gcc. Integer return values of up to 64-bits (8 bytes) are always returned directly in registers. However, when a struct is being returned, only structs of 4 bytes or less are returned in registers. If the struct is of 5 bytes or larger, avr-gcc will always pass it on the stack.

This means that different return types have different size limits for fitting into registers or the stack.
39 changes: 30 additions & 9 deletions llvm/lib/Target/AVR/AVRISelLowering.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1305,6 +1305,35 @@ 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<CCValAssign, 16> &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++;
}
}
}
} else {
std::reverse(RVLocs.begin(), RVLocs.end());
}
}
}

/// Lower the result values of a call into the
/// appropriate copies out of appropriate physical registers.
///
Expand All @@ -1322,12 +1351,6 @@ SDValue AVRTargetLowering::LowerCallResult(
auto CCFunction = CCAssignFnForReturn(CallConv);
CCInfo.AnalyzeCallResult(Ins, CCFunction);

if (CallConv != CallingConv::AVR_BUILTIN && RVLocs.size() > 1) {
// Reverse splitted return values to get the "big endian" format required
// to agree with the calling convention ABI.
std::reverse(RVLocs.begin(), RVLocs.end());
}

// 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(),
Expand Down Expand Up @@ -1390,9 +1413,7 @@ AVRTargetLowering::LowerReturn(SDValue Chain, CallingConv::ID CallConv,

// Reverse splitted return values to get the "big endian" format required
// to agree with the calling convention ABI.
if (e > 1) {
std::reverse(RVLocs.begin(), RVLocs.end());
}
ReverseArgumentsToBigEndian(MF, RVLocs);

SDValue Flag;
SmallVector<SDValue, 4> RetOps(1, Chain);
Expand Down
114 changes: 114 additions & 0 deletions llvm/test/CodeGen/AVR/calling-conv/c/return.ll
Original file line number Diff line number Diff line change
Expand Up @@ -34,3 +34,117 @@ define i64 @ret_i64() {
; CHECK-NEXT: mov r25, r19
ret i64 18374966859414961920
}

; CHECK-LABEL: ret_struct1
define { i8, i32 } @ret_struct1() {
start:
; CHECK: ldi r22, 221
; CHECK: ldi r23, 204
; CHECK: ldi r20, 187
; CHECK: ldi r21, 170
; CHECK: ldi r24, 111
ret { i8, i32 } { i8 111, i32 2864434397 } ; { 0x6f, 0xaabbccdd }
}

; CHECK-LABEL: ret_struct2
define { i8, i8, i8, i8, i8 } @ret_struct2() {
start:
ret { i8, i8, i8, i8, i8 } { i8 111, i8 221, i8 204, i8 187, i8 170 } ; { 0x6f, 0xaabbccdd }
}

; CHECK-LABEL: ret_struct3
define { i32, i32 } @ret_struct3() {
start:
ret { i32, i32 } zeroinitializer
}

; CHECK-LABEL: ret_struct4
define { i8, i64 } @ret_struct4() {
start:
ret { i8, i64 } zeroinitializer
}

; CHECK-LABEL: ret_struct5
define { i8, i32, i16 } @ret_struct5() {
start:
ret { i8, i32, i16 } zeroinitializer
}

; CHECK-LABEL: ret_struct6
define { i8, i32, i32 } @ret_struct6() {
start:
ret { i8, i32, i32 } zeroinitializer
}

; Structs of size 1 should be returned in registers.
;
; This matches avr-gcc behaviour.
;
; CHECK-LABEL: ret_struct_size1
define { i8 } @ret_struct_size1() {
; CHECK: ldi r24, 123
ret { i8 } { i8 123 }
}

; Structs of size 2 are currently returned on the stack.
;
; BUG(PR39251): avr-gcc returns all structs of four bytes or less in registers.
;
; CHECK-LABEL: ret_struct_size2
define { i8, i8 } @ret_struct_size2() {
ret { i8, i8 } { i8 123, i8 234 }
}

; Structs of size 3 are currently returned on the stack.
;
; BUG(PR39251): avr-gcc returns all structs of four bytes or less in registers.
;
; CHECK-LABEL: ret_struct_size3
define { i8, i8, i8 } @ret_struct_size3() {
ret { i8, i8, i8 } { i8 123, i8 234, i8 255 }
}

; Structs of size 4 are currently returned on the stack.
;
; BUG(PR39251): avr-gcc returns all structs of four bytes or less in registers.
;
; CHECK-LABEL: ret_struct_size4
define { i8, i8, i8, i8 } @ret_struct_size4() {
ret { i8, i8, i8, i8 } { i8 123, i8 234, i8 255, i8 11 }
}

; Structs of size 5 should be returned on the stack.
;
; This matches avr-gcc behaviour.
;
; CHECK-LABEL: ret_struct_size5
define { i8, i8, i8, i8, i8 } @ret_struct_size5() {
ret { i8, i8, i8, i8, i8 } { i8 123, i8 234, i8 255, i8 11, i8 22 }
}

; Structs of size 6 should be returned on the stack.
;
; This matches avr-gcc behaviour.
;
; CHECK-LABEL: ret_struct_size6
define { i8, i8, i8, i8, i8, i8 } @ret_struct_size6() {
ret { i8, i8, i8, i8, i8, i8 } { i8 123, i8 234, i8 255, i8 11, i8 22, i8 33 }
}

; Structs of size 7 should be returned on the stack.
;
; This matches avr-gcc behaviour.
;
; CHECK-LABEL: ret_struct_size7
define { i8, i8, i8, i8, i8, i8, i8 } @ret_struct_size7() {
ret { i8, i8, i8, i8, i8, i8, i8 } { i8 123, i8 234, i8 255, i8 11, i8 22, i8 33, i8 44 }
}

; Structs of size 8 should be returned on the stack.
;
; This matches avr-gcc behaviour.
;
; CHECK-LABEL: ret_struct_size8
define { i8, i8, i8, i8, i8, i8, i8, i8 } @ret_struct_size8() {
ret { i8, i8, i8, i8, i8, i8, i8, i8 } { i8 123, i8 234, i8 255, i8 11, i8 22, i8 33, i8 44, i8 55 }
}

0 comments on commit dfd4102

Please sign in to comment.