mirror of
https://github.com/c64scene-ar/llvm-6502.git
synced 2025-09-15 22:25:31 +00:00
Add support for ARM ldrexd/strexd intrinsics. They both use i32 register pairs
to load/store i64 values. Since there's no current support to explicitly declare such restrictions, implement it by using specific hardcoded register pairs during isel. git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@132248 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
@@ -35,6 +35,16 @@ let TargetPrefix = "arm" in { // All intrinsics start with "llvm.arm.".
|
|||||||
Intrinsic<[llvm_i32_ty], [llvm_i32_ty, llvm_i32_ty], [IntrNoMem]>;
|
Intrinsic<[llvm_i32_ty], [llvm_i32_ty, llvm_i32_ty], [IntrNoMem]>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
// Load and Store exclusive doubleword
|
||||||
|
|
||||||
|
let TargetPrefix = "arm" in { // All intrinsics start with "llvm.arm.".
|
||||||
|
def int_arm_strexd : Intrinsic<[llvm_i32_ty], [llvm_i32_ty, llvm_i32_ty,
|
||||||
|
llvm_ptr_ty], [IntrReadWriteArgMem]>;
|
||||||
|
def int_arm_ldrexd : Intrinsic<[llvm_i32_ty, llvm_i32_ty], [llvm_ptr_ty],
|
||||||
|
[IntrReadArgMem]>;
|
||||||
|
}
|
||||||
|
|
||||||
//===----------------------------------------------------------------------===//
|
//===----------------------------------------------------------------------===//
|
||||||
// VFP
|
// VFP
|
||||||
|
|
||||||
|
@@ -2691,6 +2691,111 @@ SDNode *ARMDAGToDAGISel::Select(SDNode *N) {
|
|||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case Intrinsic::arm_ldrexd: {
|
||||||
|
SDValue MemAddr = N->getOperand(2);
|
||||||
|
DebugLoc dl = N->getDebugLoc();
|
||||||
|
SDValue Chain = N->getOperand(0);
|
||||||
|
|
||||||
|
unsigned NewOpc = ARM::LDREXD;
|
||||||
|
if (Subtarget->isThumb() && Subtarget->hasThumb2())
|
||||||
|
NewOpc = ARM::t2LDREXD;
|
||||||
|
|
||||||
|
// arm_ldrexd returns a i64 value in {i32, i32}
|
||||||
|
std::vector<EVT> ResTys;
|
||||||
|
ResTys.push_back(MVT::i32);
|
||||||
|
ResTys.push_back(MVT::i32);
|
||||||
|
ResTys.push_back(MVT::Other);
|
||||||
|
|
||||||
|
// place arguments in the right order
|
||||||
|
SmallVector<SDValue, 7> Ops;
|
||||||
|
Ops.push_back(MemAddr);
|
||||||
|
Ops.push_back(getAL(CurDAG));
|
||||||
|
Ops.push_back(CurDAG->getRegister(0, MVT::i32));
|
||||||
|
Ops.push_back(Chain);
|
||||||
|
SDNode *Ld = CurDAG->getMachineNode(NewOpc, dl, ResTys, Ops.data(),
|
||||||
|
Ops.size());
|
||||||
|
// Transfer memoperands.
|
||||||
|
MachineSDNode::mmo_iterator MemOp = MF->allocateMemRefsArray(1);
|
||||||
|
MemOp[0] = cast<MemIntrinsicSDNode>(N)->getMemOperand();
|
||||||
|
cast<MachineSDNode>(Ld)->setMemRefs(MemOp, MemOp + 1);
|
||||||
|
|
||||||
|
// Until there's support for specifing explicit register constraints
|
||||||
|
// like the use of even/odd register pair, hardcode ldrexd to always
|
||||||
|
// use the pair [R0, R1] to hold the load result.
|
||||||
|
Chain = CurDAG->getCopyToReg(CurDAG->getEntryNode(), dl, ARM::R0,
|
||||||
|
SDValue(Ld, 0), SDValue(0,0));
|
||||||
|
Chain = CurDAG->getCopyToReg(Chain, dl, ARM::R1,
|
||||||
|
SDValue(Ld, 1), Chain.getValue(1));
|
||||||
|
|
||||||
|
// Remap uses.
|
||||||
|
SDValue Glue = Chain.getValue(1);
|
||||||
|
if (!SDValue(N, 0).use_empty()) {
|
||||||
|
SDValue Result = CurDAG->getCopyFromReg(CurDAG->getEntryNode(), dl,
|
||||||
|
ARM::R0, MVT::i32, Glue);
|
||||||
|
Glue = Result.getValue(2);
|
||||||
|
ReplaceUses(SDValue(N, 0), Result);
|
||||||
|
}
|
||||||
|
if (!SDValue(N, 1).use_empty()) {
|
||||||
|
SDValue Result = CurDAG->getCopyFromReg(CurDAG->getEntryNode(), dl,
|
||||||
|
ARM::R1, MVT::i32, Glue);
|
||||||
|
Glue = Result.getValue(2);
|
||||||
|
ReplaceUses(SDValue(N, 1), Result);
|
||||||
|
}
|
||||||
|
|
||||||
|
ReplaceUses(SDValue(N, 2), SDValue(Ld, 2));
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
case Intrinsic::arm_strexd: {
|
||||||
|
DebugLoc dl = N->getDebugLoc();
|
||||||
|
SDValue Chain = N->getOperand(0);
|
||||||
|
SDValue Val0 = N->getOperand(2);
|
||||||
|
SDValue Val1 = N->getOperand(3);
|
||||||
|
SDValue MemAddr = N->getOperand(4);
|
||||||
|
|
||||||
|
// Until there's support for specifing explicit register constraints
|
||||||
|
// like the use of even/odd register pair, hardcode strexd to always
|
||||||
|
// use the pair [R2, R3] to hold the i64 (i32, i32) value to be stored.
|
||||||
|
Chain = CurDAG->getCopyToReg(CurDAG->getEntryNode(), dl, ARM::R2, Val0,
|
||||||
|
SDValue(0, 0));
|
||||||
|
Chain = CurDAG->getCopyToReg(Chain, dl, ARM::R3, Val1, Chain.getValue(1));
|
||||||
|
|
||||||
|
SDValue Glue = Chain.getValue(1);
|
||||||
|
Val0 = CurDAG->getCopyFromReg(CurDAG->getEntryNode(), dl,
|
||||||
|
ARM::R2, MVT::i32, Glue);
|
||||||
|
Glue = Val0.getValue(1);
|
||||||
|
Val1 = CurDAG->getCopyFromReg(CurDAG->getEntryNode(), dl,
|
||||||
|
ARM::R3, MVT::i32, Glue);
|
||||||
|
|
||||||
|
// Store exclusive double return a i32 value which is the return status
|
||||||
|
// of the issued store.
|
||||||
|
std::vector<EVT> ResTys;
|
||||||
|
ResTys.push_back(MVT::i32);
|
||||||
|
ResTys.push_back(MVT::Other);
|
||||||
|
|
||||||
|
// place arguments in the right order
|
||||||
|
SmallVector<SDValue, 7> Ops;
|
||||||
|
Ops.push_back(Val0);
|
||||||
|
Ops.push_back(Val1);
|
||||||
|
Ops.push_back(MemAddr);
|
||||||
|
Ops.push_back(getAL(CurDAG));
|
||||||
|
Ops.push_back(CurDAG->getRegister(0, MVT::i32));
|
||||||
|
Ops.push_back(Chain);
|
||||||
|
|
||||||
|
unsigned NewOpc = ARM::STREXD;
|
||||||
|
if (Subtarget->isThumb() && Subtarget->hasThumb2())
|
||||||
|
NewOpc = ARM::t2STREXD;
|
||||||
|
|
||||||
|
SDNode *St = CurDAG->getMachineNode(NewOpc, dl, ResTys, Ops.data(),
|
||||||
|
Ops.size());
|
||||||
|
// Transfer memoperands.
|
||||||
|
MachineSDNode::mmo_iterator MemOp = MF->allocateMemRefsArray(1);
|
||||||
|
MemOp[0] = cast<MemIntrinsicSDNode>(N)->getMemOperand();
|
||||||
|
cast<MachineSDNode>(St)->setMemRefs(MemOp, MemOp + 1);
|
||||||
|
|
||||||
|
return St;
|
||||||
|
}
|
||||||
|
|
||||||
case Intrinsic::arm_neon_vld1: {
|
case Intrinsic::arm_neon_vld1: {
|
||||||
unsigned DOpcodes[] = { ARM::VLD1d8, ARM::VLD1d16,
|
unsigned DOpcodes[] = { ARM::VLD1d8, ARM::VLD1d16,
|
||||||
ARM::VLD1d32, ARM::VLD1d64 };
|
ARM::VLD1d32, ARM::VLD1d64 };
|
||||||
|
@@ -7658,6 +7658,28 @@ bool ARMTargetLowering::getTgtMemIntrinsic(IntrinsicInfo &Info,
|
|||||||
Info.writeMem = true;
|
Info.writeMem = true;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
case Intrinsic::arm_strexd: {
|
||||||
|
Info.opc = ISD::INTRINSIC_W_CHAIN;
|
||||||
|
Info.memVT = MVT::i64;
|
||||||
|
Info.ptrVal = I.getArgOperand(2);
|
||||||
|
Info.offset = 0;
|
||||||
|
Info.align = 8;
|
||||||
|
Info.vol = false;
|
||||||
|
Info.readMem = false;
|
||||||
|
Info.writeMem = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
case Intrinsic::arm_ldrexd: {
|
||||||
|
Info.opc = ISD::INTRINSIC_W_CHAIN;
|
||||||
|
Info.memVT = MVT::i64;
|
||||||
|
Info.ptrVal = I.getArgOperand(0);
|
||||||
|
Info.offset = 0;
|
||||||
|
Info.align = 8;
|
||||||
|
Info.vol = false;
|
||||||
|
Info.readMem = true;
|
||||||
|
Info.writeMem = false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@@ -3366,7 +3366,8 @@ def LDREXH : AIldrex<0b11, (outs GPR:$Rt), (ins addrmode7:$addr), NoItinerary,
|
|||||||
"ldrexh", "\t$Rt, $addr", []>;
|
"ldrexh", "\t$Rt, $addr", []>;
|
||||||
def LDREX : AIldrex<0b00, (outs GPR:$Rt), (ins addrmode7:$addr), NoItinerary,
|
def LDREX : AIldrex<0b00, (outs GPR:$Rt), (ins addrmode7:$addr), NoItinerary,
|
||||||
"ldrex", "\t$Rt, $addr", []>;
|
"ldrex", "\t$Rt, $addr", []>;
|
||||||
def LDREXD : AIldrex<0b01, (outs GPR:$Rt, GPR:$Rt2), (ins addrmode7:$addr),
|
let hasExtraDefRegAllocReq = 1 in
|
||||||
|
def LDREXD : AIldrex<0b01, (outs GPR:$Rt, GPR:$Rt2), (ins addrmode7:$addr),
|
||||||
NoItinerary, "ldrexd", "\t$Rt, $Rt2, $addr", []>;
|
NoItinerary, "ldrexd", "\t$Rt, $Rt2, $addr", []>;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3377,10 +3378,12 @@ def STREXH : AIstrex<0b11, (outs GPR:$Rd), (ins GPR:$Rt, addrmode7:$addr),
|
|||||||
NoItinerary, "strexh", "\t$Rd, $Rt, $addr", []>;
|
NoItinerary, "strexh", "\t$Rd, $Rt, $addr", []>;
|
||||||
def STREX : AIstrex<0b00, (outs GPR:$Rd), (ins GPR:$Rt, addrmode7:$addr),
|
def STREX : AIstrex<0b00, (outs GPR:$Rd), (ins GPR:$Rt, addrmode7:$addr),
|
||||||
NoItinerary, "strex", "\t$Rd, $Rt, $addr", []>;
|
NoItinerary, "strex", "\t$Rd, $Rt, $addr", []>;
|
||||||
|
}
|
||||||
|
|
||||||
|
let hasExtraSrcRegAllocReq = 1, Constraints = "@earlyclobber $Rd" in
|
||||||
def STREXD : AIstrex<0b01, (outs GPR:$Rd),
|
def STREXD : AIstrex<0b01, (outs GPR:$Rd),
|
||||||
(ins GPR:$Rt, GPR:$Rt2, addrmode7:$addr),
|
(ins GPR:$Rt, GPR:$Rt2, addrmode7:$addr),
|
||||||
NoItinerary, "strexd", "\t$Rd, $Rt, $Rt2, $addr", []>;
|
NoItinerary, "strexd", "\t$Rd, $Rt, $Rt2, $addr", []>;
|
||||||
}
|
|
||||||
|
|
||||||
// Clear-Exclusive is for disassembly only.
|
// Clear-Exclusive is for disassembly only.
|
||||||
def CLREX : AXI<(outs), (ins), MiscFrm, NoItinerary, "clrex",
|
def CLREX : AXI<(outs), (ins), MiscFrm, NoItinerary, "clrex",
|
||||||
|
@@ -2881,7 +2881,9 @@ def t2LDREX : Thumb2I<(outs rGPR:$Rt), (ins t2addrmode_reg:$addr), AddrModeNone
|
|||||||
let Inst{19-16} = addr;
|
let Inst{19-16} = addr;
|
||||||
let Inst{15-12} = Rt;
|
let Inst{15-12} = Rt;
|
||||||
}
|
}
|
||||||
def t2LDREXD : T2I_ldrex<0b11, (outs rGPR:$Rt, rGPR:$Rt2), (ins t2addrmode_reg:$addr),
|
let hasExtraDefRegAllocReq = 1 in
|
||||||
|
def t2LDREXD : T2I_ldrex<0b11, (outs rGPR:$Rt, rGPR:$Rt2),
|
||||||
|
(ins t2addrmode_reg:$addr),
|
||||||
AddrModeNone, Size4Bytes, NoItinerary,
|
AddrModeNone, Size4Bytes, NoItinerary,
|
||||||
"ldrexd", "\t$Rt, $Rt2, $addr", "",
|
"ldrexd", "\t$Rt, $Rt2, $addr", "",
|
||||||
[], {?, ?, ?, ?}> {
|
[], {?, ?, ?, ?}> {
|
||||||
@@ -2912,6 +2914,9 @@ def t2STREX : Thumb2I<(outs rGPR:$Rd), (ins rGPR:$Rt, t2addrmode_reg:$addr),
|
|||||||
let Inst{19-16} = addr;
|
let Inst{19-16} = addr;
|
||||||
let Inst{15-12} = Rt;
|
let Inst{15-12} = Rt;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let hasExtraSrcRegAllocReq = 1, Constraints = "@earlyclobber $Rd" in
|
||||||
def t2STREXD : T2I_strex<0b11, (outs rGPR:$Rd),
|
def t2STREXD : T2I_strex<0b11, (outs rGPR:$Rd),
|
||||||
(ins rGPR:$Rt, rGPR:$Rt2, t2addrmode_reg:$addr),
|
(ins rGPR:$Rt, rGPR:$Rt2, t2addrmode_reg:$addr),
|
||||||
AddrModeNone, Size4Bytes, NoItinerary,
|
AddrModeNone, Size4Bytes, NoItinerary,
|
||||||
@@ -2920,7 +2925,6 @@ def t2STREXD : T2I_strex<0b11, (outs rGPR:$Rd),
|
|||||||
bits<4> Rt2;
|
bits<4> Rt2;
|
||||||
let Inst{11-8} = Rt2;
|
let Inst{11-8} = Rt2;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Clear-Exclusive is for disassembly only.
|
// Clear-Exclusive is for disassembly only.
|
||||||
def t2CLREX : T2XI<(outs), (ins), NoItinerary, "clrex",
|
def t2CLREX : T2XI<(outs), (ins), NoItinerary, "clrex",
|
||||||
|
33
test/CodeGen/ARM/ldstrexd.ll
Normal file
33
test/CodeGen/ARM/ldstrexd.ll
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
; RUN: llc < %s -mtriple=armv7-apple-darwin | FileCheck %s
|
||||||
|
; RUN: llc < %s -mtriple=thumbv7-apple-darwin | FileCheck %s
|
||||||
|
|
||||||
|
%0 = type { i32, i32 }
|
||||||
|
|
||||||
|
; CHECK: f0:
|
||||||
|
; CHECK: ldrexd
|
||||||
|
define i64 @f0(i8* %p) nounwind readonly {
|
||||||
|
entry:
|
||||||
|
%ldrexd = tail call %0 @llvm.arm.ldrexd(i8* %p)
|
||||||
|
%0 = extractvalue %0 %ldrexd, 1
|
||||||
|
%1 = extractvalue %0 %ldrexd, 0
|
||||||
|
%2 = zext i32 %0 to i64
|
||||||
|
%3 = zext i32 %1 to i64
|
||||||
|
%shl = shl nuw i64 %2, 32
|
||||||
|
%4 = or i64 %shl, %3
|
||||||
|
ret i64 %4
|
||||||
|
}
|
||||||
|
|
||||||
|
; CHECK: f1:
|
||||||
|
; CHECK: strexd
|
||||||
|
define i32 @f1(i8* %ptr, i64 %val) nounwind {
|
||||||
|
entry:
|
||||||
|
%tmp4 = trunc i64 %val to i32
|
||||||
|
%tmp6 = lshr i64 %val, 32
|
||||||
|
%tmp7 = trunc i64 %tmp6 to i32
|
||||||
|
%strexd = tail call i32 @llvm.arm.strexd(i32 %tmp4, i32 %tmp7, i8* %ptr)
|
||||||
|
ret i32 %strexd
|
||||||
|
}
|
||||||
|
|
||||||
|
declare %0 @llvm.arm.ldrexd(i8*) nounwind readonly
|
||||||
|
declare i32 @llvm.arm.strexd(i32, i32, i8*) nounwind
|
||||||
|
|
Reference in New Issue
Block a user