mirror of
https://github.com/c64scene-ar/llvm-6502.git
synced 2025-09-30 06:19:45 +00:00
Fix Thumb2 function call isel. Thumb1 and Thumb2 should share the same
instructions for calls since BL and BLX are always 32-bit long and BX is always 16-bit long. Also, we should be using BLX to call external function stubs. git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@77756 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
@@ -263,7 +263,7 @@ ARMTargetLowering::ARMTargetLowering(TargetMachine &TM)
|
|||||||
} else {
|
} else {
|
||||||
setOperationAction(ISD::MUL, MVT::i64, Expand);
|
setOperationAction(ISD::MUL, MVT::i64, Expand);
|
||||||
setOperationAction(ISD::MULHU, MVT::i32, Expand);
|
setOperationAction(ISD::MULHU, MVT::i32, Expand);
|
||||||
if (!Subtarget->isThumb1Only() && !Subtarget->hasV6Ops())
|
if (!Subtarget->hasV6Ops())
|
||||||
setOperationAction(ISD::MULHS, MVT::i32, Expand);
|
setOperationAction(ISD::MULHS, MVT::i32, Expand);
|
||||||
}
|
}
|
||||||
setOperationAction(ISD::SHL_PARTS, MVT::i32, Expand);
|
setOperationAction(ISD::SHL_PARTS, MVT::i32, Expand);
|
||||||
@@ -985,8 +985,8 @@ SDValue ARMTargetLowering::LowerCALL(SDValue Op, SelectionDAG &DAG) {
|
|||||||
|
|
||||||
// FIXME: handle tail calls differently.
|
// FIXME: handle tail calls differently.
|
||||||
unsigned CallOpc;
|
unsigned CallOpc;
|
||||||
if (Subtarget->isThumb1Only()) {
|
if (Subtarget->isThumb()) {
|
||||||
if (!Subtarget->hasV5TOps() && (!isDirect || isARMFunc))
|
if ((!isDirect || isARMFunc) && !Subtarget->hasV5TOps())
|
||||||
CallOpc = ARMISD::CALL_NOLINK;
|
CallOpc = ARMISD::CALL_NOLINK;
|
||||||
else
|
else
|
||||||
CallOpc = isARMFunc ? ARMISD::CALL : ARMISD::tCALL;
|
CallOpc = isARMFunc ? ARMISD::CALL : ARMISD::tCALL;
|
||||||
|
@@ -808,7 +808,7 @@ class ThumbI<dag oops, dag iops, AddrMode am, SizeFlagVal sz,
|
|||||||
class TI<dag oops, dag iops, string asm, list<dag> pattern>
|
class TI<dag oops, dag iops, string asm, list<dag> pattern>
|
||||||
: ThumbI<oops, iops, AddrModeNone, Size2Bytes, asm, "", pattern>;
|
: ThumbI<oops, iops, AddrModeNone, Size2Bytes, asm, "", pattern>;
|
||||||
|
|
||||||
// BL, BLX(1) are translated by assembler into two instructions
|
// tBL, tBX instructions
|
||||||
class TIx2<dag oops, dag iops, string asm, list<dag> pattern>
|
class TIx2<dag oops, dag iops, string asm, list<dag> pattern>
|
||||||
: ThumbI<oops, iops, AddrModeNone, Size4Bytes, asm, "", pattern>;
|
: ThumbI<oops, iops, AddrModeNone, Size4Bytes, asm, "", pattern>;
|
||||||
|
|
||||||
|
@@ -170,24 +170,26 @@ let isCall = 1,
|
|||||||
D0, D1, D2, D3, D4, D5, D6, D7,
|
D0, D1, D2, D3, D4, D5, D6, D7,
|
||||||
D16, D17, D18, D19, D20, D21, D22, D23,
|
D16, D17, D18, D19, D20, D21, D22, D23,
|
||||||
D24, D25, D26, D27, D28, D29, D30, D31, CPSR] in {
|
D24, D25, D26, D27, D28, D29, D30, D31, CPSR] in {
|
||||||
def tBL : T1Ix2<(outs), (ins i32imm:$func, variable_ops),
|
// Also used for Thumb2
|
||||||
|
def tBL : TIx2<(outs), (ins i32imm:$func, variable_ops),
|
||||||
"bl ${func:call}",
|
"bl ${func:call}",
|
||||||
[(ARMtcall tglobaladdr:$func)]>,
|
[(ARMtcall tglobaladdr:$func)]>,
|
||||||
Requires<[IsThumb1Only, IsNotDarwin]>;
|
Requires<[IsThumb, IsNotDarwin]>;
|
||||||
|
|
||||||
// ARMv5T and above
|
// ARMv5T and above, also used for Thumb2
|
||||||
def tBLXi : T1Ix2<(outs), (ins i32imm:$func, variable_ops),
|
def tBLXi : TIx2<(outs), (ins i32imm:$func, variable_ops),
|
||||||
"blx ${func:call}",
|
"blx ${func:call}",
|
||||||
[(ARMcall tglobaladdr:$func)]>,
|
[(ARMcall tglobaladdr:$func)]>,
|
||||||
Requires<[IsThumb1Only, HasV5T, IsNotDarwin]>;
|
Requires<[IsThumb, HasV5T, IsNotDarwin]>;
|
||||||
|
|
||||||
def tBLXr : T1I<(outs), (ins tGPR:$func, variable_ops),
|
// Also used for Thumb2
|
||||||
|
def tBLXr : TI<(outs), (ins GPR:$func, variable_ops),
|
||||||
"blx $func",
|
"blx $func",
|
||||||
[(ARMtcall tGPR:$func)]>,
|
[(ARMtcall GPR:$func)]>,
|
||||||
Requires<[IsThumb1Only, HasV5T, IsNotDarwin]>;
|
Requires<[IsThumb, HasV5T, IsNotDarwin]>;
|
||||||
|
|
||||||
// ARMv4T
|
// ARMv4T
|
||||||
def tBX : T1Ix2<(outs), (ins tGPR:$func, variable_ops),
|
def tBX : TIx2<(outs), (ins tGPR:$func, variable_ops),
|
||||||
"mov lr, pc\n\tbx $func",
|
"mov lr, pc\n\tbx $func",
|
||||||
[(ARMcall_nolink tGPR:$func)]>,
|
[(ARMcall_nolink tGPR:$func)]>,
|
||||||
Requires<[IsThumb1Only, IsNotDarwin]>;
|
Requires<[IsThumb1Only, IsNotDarwin]>;
|
||||||
@@ -199,24 +201,26 @@ let isCall = 1,
|
|||||||
D0, D1, D2, D3, D4, D5, D6, D7,
|
D0, D1, D2, D3, D4, D5, D6, D7,
|
||||||
D16, D17, D18, D19, D20, D21, D22, D23,
|
D16, D17, D18, D19, D20, D21, D22, D23,
|
||||||
D24, D25, D26, D27, D28, D29, D30, D31, CPSR] in {
|
D24, D25, D26, D27, D28, D29, D30, D31, CPSR] in {
|
||||||
def tBLr9 : T1Ix2<(outs), (ins i32imm:$func, variable_ops),
|
// Also used for Thumb2
|
||||||
|
def tBLr9 : TIx2<(outs), (ins i32imm:$func, variable_ops),
|
||||||
"bl ${func:call}",
|
"bl ${func:call}",
|
||||||
[(ARMtcall tglobaladdr:$func)]>,
|
[(ARMtcall tglobaladdr:$func)]>,
|
||||||
Requires<[IsThumb1Only, IsDarwin]>;
|
Requires<[IsThumb, IsDarwin]>;
|
||||||
|
|
||||||
// ARMv5T and above
|
// ARMv5T and above, also used for Thumb2
|
||||||
def tBLXi_r9 : T1Ix2<(outs), (ins i32imm:$func, variable_ops),
|
def tBLXi_r9 : TIx2<(outs), (ins i32imm:$func, variable_ops),
|
||||||
"blx ${func:call}",
|
"blx ${func:call}",
|
||||||
[(ARMcall tglobaladdr:$func)]>,
|
[(ARMcall tglobaladdr:$func)]>,
|
||||||
Requires<[IsThumb1Only, HasV5T, IsDarwin]>;
|
Requires<[IsThumb, HasV5T, IsDarwin]>;
|
||||||
|
|
||||||
def tBLXr_r9 : T1I<(outs), (ins tGPR:$func, variable_ops),
|
// Also used for Thumb2
|
||||||
|
def tBLXr_r9 : TI<(outs), (ins GPR:$func, variable_ops),
|
||||||
"blx $func",
|
"blx $func",
|
||||||
[(ARMtcall tGPR:$func)]>,
|
[(ARMtcall GPR:$func)]>,
|
||||||
Requires<[IsThumb1Only, HasV5T, IsDarwin]>;
|
Requires<[IsThumb, HasV5T, IsDarwin]>;
|
||||||
|
|
||||||
// ARMv4T
|
// ARMv4T
|
||||||
def tBXr9 : T1Ix2<(outs), (ins tGPR:$func, variable_ops),
|
def tBXr9 : TIx2<(outs), (ins tGPR:$func, variable_ops),
|
||||||
"mov lr, pc\n\tbx $func",
|
"mov lr, pc\n\tbx $func",
|
||||||
[(ARMcall_nolink tGPR:$func)]>,
|
[(ARMcall_nolink tGPR:$func)]>,
|
||||||
Requires<[IsThumb1Only, IsDarwin]>;
|
Requires<[IsThumb1Only, IsDarwin]>;
|
||||||
@@ -229,7 +233,7 @@ let isBranch = 1, isTerminator = 1 in {
|
|||||||
[(br bb:$target)]>;
|
[(br bb:$target)]>;
|
||||||
|
|
||||||
// Far jump
|
// Far jump
|
||||||
def tBfar : T1Ix2<(outs), (ins brtarget:$target),
|
def tBfar : TIx2<(outs), (ins brtarget:$target),
|
||||||
"bl $target\t@ far jump",[]>;
|
"bl $target\t@ far jump",[]>;
|
||||||
|
|
||||||
def tBR_JTr : T1JTI<(outs),
|
def tBR_JTr : T1JTI<(outs),
|
||||||
@@ -601,7 +605,7 @@ def tLEApcrelJT : T1I<(outs tGPR:$dst), (ins i32imm:$label, i32imm:$id),
|
|||||||
// __aeabi_read_tp preserves the registers r1-r3.
|
// __aeabi_read_tp preserves the registers r1-r3.
|
||||||
let isCall = 1,
|
let isCall = 1,
|
||||||
Defs = [R0, LR] in {
|
Defs = [R0, LR] in {
|
||||||
def tTPsoft : T1Ix2<(outs), (ins),
|
def tTPsoft : TIx2<(outs), (ins),
|
||||||
"bl __aeabi_read_tp",
|
"bl __aeabi_read_tp",
|
||||||
[(set R0, ARMthread_pointer)]>;
|
[(set R0, ARMthread_pointer)]>;
|
||||||
}
|
}
|
||||||
@@ -636,20 +640,20 @@ def : T1Pat<(ARMWrapperJT tjumptable:$dst, imm:$id),
|
|||||||
|
|
||||||
// Direct calls
|
// Direct calls
|
||||||
def : T1Pat<(ARMtcall texternalsym:$func), (tBL texternalsym:$func)>,
|
def : T1Pat<(ARMtcall texternalsym:$func), (tBL texternalsym:$func)>,
|
||||||
Requires<[IsThumb1Only, IsNotDarwin]>;
|
Requires<[IsThumb, IsNotDarwin]>;
|
||||||
def : T1Pat<(ARMtcall texternalsym:$func), (tBLr9 texternalsym:$func)>,
|
def : T1Pat<(ARMtcall texternalsym:$func), (tBLr9 texternalsym:$func)>,
|
||||||
Requires<[IsThumb1Only, IsDarwin]>;
|
Requires<[IsThumb, IsDarwin]>;
|
||||||
|
|
||||||
def : Tv5Pat<(ARMcall texternalsym:$func), (tBLXi texternalsym:$func)>,
|
def : Tv5Pat<(ARMcall texternalsym:$func), (tBLXi texternalsym:$func)>,
|
||||||
Requires<[IsThumb1Only, HasV5T, IsNotDarwin]>;
|
Requires<[IsThumb, HasV5T, IsNotDarwin]>;
|
||||||
def : Tv5Pat<(ARMcall texternalsym:$func), (tBLXi_r9 texternalsym:$func)>,
|
def : Tv5Pat<(ARMcall texternalsym:$func), (tBLXi_r9 texternalsym:$func)>,
|
||||||
Requires<[IsThumb1Only, HasV5T, IsDarwin]>;
|
Requires<[IsThumb, HasV5T, IsDarwin]>;
|
||||||
|
|
||||||
// Indirect calls to ARM routines
|
// Indirect calls to ARM routines
|
||||||
def : Tv5Pat<(ARMcall tGPR:$dst), (tBLXr tGPR:$dst)>,
|
def : Tv5Pat<(ARMcall GPR:$dst), (tBLXr GPR:$dst)>,
|
||||||
Requires<[IsThumb1Only, HasV5T, IsNotDarwin]>;
|
Requires<[IsThumb, HasV5T, IsNotDarwin]>;
|
||||||
def : Tv5Pat<(ARMcall tGPR:$dst), (tBLXr_r9 tGPR:$dst)>,
|
def : Tv5Pat<(ARMcall GPR:$dst), (tBLXr_r9 GPR:$dst)>,
|
||||||
Requires<[IsThumb1Only, HasV5T, IsDarwin]>;
|
Requires<[IsThumb, HasV5T, IsDarwin]>;
|
||||||
|
|
||||||
// zextload i1 -> zextload i8
|
// zextload i1 -> zextload i8
|
||||||
def : T1Pat<(zextloadi1 t_addrmode_s1:$addr),
|
def : T1Pat<(zextloadi1 t_addrmode_s1:$addr),
|
||||||
|
@@ -1016,40 +1016,6 @@ let isReturn = 1, isTerminator = 1, mayLoad = 1 in
|
|||||||
"ldm${addr:submode}${p} $addr, $dst1",
|
"ldm${addr:submode}${p} $addr, $dst1",
|
||||||
[]>;
|
[]>;
|
||||||
|
|
||||||
// On non-Darwin platforms R9 is callee-saved.
|
|
||||||
let isCall = 1,
|
|
||||||
Defs = [R0, R1, R2, R3, R12, LR,
|
|
||||||
D0, D1, D2, D3, D4, D5, D6, D7,
|
|
||||||
D16, D17, D18, D19, D20, D21, D22, D23,
|
|
||||||
D24, D25, D26, D27, D28, D29, D30, D31, CPSR] in {
|
|
||||||
def t2BL : T2XI<(outs), (ins i32imm:$func, variable_ops),
|
|
||||||
"bl ${func:call}",
|
|
||||||
[(ARMcall tglobaladdr:$func)]>,
|
|
||||||
Requires<[IsThumb2, IsNotDarwin]>;
|
|
||||||
|
|
||||||
def t2BLX : T2XI<(outs), (ins GPR:$func, variable_ops),
|
|
||||||
"blx $func",
|
|
||||||
[(ARMcall GPR:$func)]>,
|
|
||||||
Requires<[IsThumb2, IsNotDarwin]>;
|
|
||||||
}
|
|
||||||
|
|
||||||
// On Darwin R9 is call-clobbered.
|
|
||||||
let isCall = 1,
|
|
||||||
Defs = [R0, R1, R2, R3, R9, R12, LR,
|
|
||||||
D0, D1, D2, D3, D4, D5, D6, D7,
|
|
||||||
D16, D17, D18, D19, D20, D21, D22, D23,
|
|
||||||
D24, D25, D26, D27, D28, D29, D30, D31, CPSR] in {
|
|
||||||
def t2BLr9 : T2XI<(outs), (ins i32imm:$func, variable_ops),
|
|
||||||
"bl ${func:call}",
|
|
||||||
[(ARMcall tglobaladdr:$func)]>,
|
|
||||||
Requires<[IsThumb2, IsDarwin]>;
|
|
||||||
|
|
||||||
def t2BLXr9 : T2XI<(outs), (ins GPR:$func, variable_ops),
|
|
||||||
"blx $func",
|
|
||||||
[(ARMcall GPR:$func)]>,
|
|
||||||
Requires<[IsThumb2, IsDarwin]>;
|
|
||||||
}
|
|
||||||
|
|
||||||
let isBranch = 1, isTerminator = 1, isBarrier = 1 in {
|
let isBranch = 1, isTerminator = 1, isBarrier = 1 in {
|
||||||
let isPredicable = 1 in
|
let isPredicable = 1 in
|
||||||
def t2B : T2XI<(outs), (ins brtarget:$target),
|
def t2B : T2XI<(outs), (ins brtarget:$target),
|
||||||
@@ -1103,9 +1069,3 @@ def : T2Pat<(ARMWrapperJT tjumptable:$dst, imm:$id),
|
|||||||
|
|
||||||
def : T2Pat<(i32 imm:$src),
|
def : T2Pat<(i32 imm:$src),
|
||||||
(t2MOVTi16 (t2MOVi16 (t2_lo16 imm:$src)), (t2_hi16 imm:$src))>;
|
(t2MOVTi16 (t2MOVi16 (t2_lo16 imm:$src)), (t2_hi16 imm:$src))>;
|
||||||
|
|
||||||
// Direct calls
|
|
||||||
def : T2Pat<(ARMcall texternalsym:$func), (t2BL texternalsym:$func)>,
|
|
||||||
Requires<[IsThumb2, IsNotDarwin]>;
|
|
||||||
def : T2Pat<(ARMcall texternalsym:$func), (t2BLr9 texternalsym:$func)>,
|
|
||||||
Requires<[IsThumb2, IsDarwin]>;
|
|
||||||
|
@@ -75,6 +75,10 @@ ARMSubtarget::ARMSubtarget(const Module &M, const std::string &FS,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Thumb2 implies at least V6T2.
|
||||||
|
if (ARMArchVersion < V6T2 && ThumbMode >= Thumb2)
|
||||||
|
ARMArchVersion = V6T2;
|
||||||
|
|
||||||
if (Len >= 10) {
|
if (Len >= 10) {
|
||||||
if (TT.find("-darwin") != std::string::npos)
|
if (TT.find("-darwin") != std::string::npos)
|
||||||
// arm-darwin
|
// arm-darwin
|
||||||
|
20
test/CodeGen/Thumb2/thumb2-call.ll
Normal file
20
test/CodeGen/Thumb2/thumb2-call.ll
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
; RUN: llvm-as < %s | llc -march=thumb -mattr=+thumb2 | FileCheck %s
|
||||||
|
|
||||||
|
@t = weak global i32 ()* null ; <i32 ()**> [#uses=1]
|
||||||
|
|
||||||
|
declare void @g(i32, i32, i32, i32)
|
||||||
|
|
||||||
|
define void @f() {
|
||||||
|
; CHECK: f:
|
||||||
|
; CHECK: blx
|
||||||
|
call void @g( i32 1, i32 2, i32 3, i32 4 )
|
||||||
|
ret void
|
||||||
|
}
|
||||||
|
|
||||||
|
define void @h() {
|
||||||
|
; CHECK: h:
|
||||||
|
; CHECK: blx r0
|
||||||
|
%tmp = load i32 ()** @t ; <i32 ()*> [#uses=1]
|
||||||
|
%tmp.upgrd.2 = tail call i32 %tmp( ) ; <i32> [#uses=0]
|
||||||
|
ret void
|
||||||
|
}
|
@@ -1,4 +1,4 @@
|
|||||||
; RUN: llvm-as < %s | llc -march=thumb -mattr=+thumb2 | grep smull | count 1
|
; RUN: llvm-as < %s | llc -march=thumb -mattr=+thumb2 | grep smmul | count 1
|
||||||
; RUN: llvm-as < %s | llc -march=thumb -mattr=+thumb2 | grep umull | count 1
|
; RUN: llvm-as < %s | llc -march=thumb -mattr=+thumb2 | grep umull | count 1
|
||||||
|
|
||||||
define i32 @smulhi(i32 %x, i32 %y) {
|
define i32 @smulhi(i32 %x, i32 %y) {
|
||||||
|
Reference in New Issue
Block a user