diff --git a/lib/Target/AVR/AVRExpandPseudoInsts.cpp b/lib/Target/AVR/AVRExpandPseudoInsts.cpp index a2be0d42b79..f3bdb674c96 100644 --- a/lib/Target/AVR/AVRExpandPseudoInsts.cpp +++ b/lib/Target/AVR/AVRExpandPseudoInsts.cpp @@ -15,6 +15,7 @@ #include "llvm/CodeGen/MachineFunctionPass.h" #include "llvm/CodeGen/MachineInstrBuilder.h" +#include "llvm/CodeGen/MachineRegisterInfo.h" #include "llvm/Target/TargetRegisterInfo.h" #include "AVR.h" @@ -30,8 +31,15 @@ namespace llvm { class AVRExpandPseudo : public MachineFunctionPass { public: static char ID; + const AVRRegisterInfo *TRI; const TargetInstrInfo *TII; + + /// The register to be used for temporary storage. + const unsigned SCRATCH_REGISTER = AVR::R0; + /// The IO address of the status register. + const unsigned SREG_ADDR = 0x3f; + AVRExpandPseudo() : MachineFunctionPass(ID) {} bool runOnMachineFunction(MachineFunction &MF) override; @@ -57,6 +65,21 @@ class AVRExpandPseudo : public MachineFunctionPass { unsigned DstReg) { return BuildMI(MBB, MBBI, MBBI->getDebugLoc(), TII->get(Opcode), DstReg); } + + MachineRegisterInfo &getRegInfo(Block &MBB) { return MBB.getParent()->getRegInfo(); } + + template + bool expandAtomic(Block &MBB, BlockIt MBBI, Func f); + + template + bool expandAtomicBinaryOp(unsigned Opcode, Block &MBB, BlockIt MBBI, Func f); + + bool expandAtomicBinaryOp(unsigned Opcode, Block &MBB, BlockIt MBBI); + + bool expandAtomicArithmeticOp(unsigned MemOpcode, + unsigned ArithOpcode, + Block &MBB, + BlockIt MBBI); }; char AVRExpandPseudo::ID = 0; @@ -80,9 +103,20 @@ bool AVRExpandPseudo::runOnMachineFunction(MachineFunction &MF) { TRI = static_cast(TM.getSubtargetImpl()->getRegisterInfo()); TII = TM.getSubtargetImpl()->getInstrInfo(); - typedef MachineFunction::iterator FuncIt; - for (FuncIt MFI = MF.begin(), E = MF.end(); MFI != E; ++MFI) { - Modified |= expandMBB(*MFI); + for(Block &MBB : MF) { + bool ContinueExpanding = true; + unsigned ExpandCount = 0; + + // Continue expanding the block until all pseudos are expanded. + do { + assert(ExpandCount < 10 && "pseudo expand limit reached"); + + bool BlockModified = expandMBB(MBB); + Modified |= BlockModified; + ExpandCount++; + + ContinueExpanding = BlockModified; + } while(ContinueExpanding); } return Modified; @@ -799,6 +833,146 @@ bool AVRExpandPseudo::expand(Block &MBB, BlockIt MBBI) { return true; } +template +bool AVRExpandPseudo::expandAtomic(Block &MBB, BlockIt MBBI, Func f) { + // Remove the pseudo instruction. + MachineInstr &MI = *MBBI; + + // Store the SREG. + buildMI(MBB, MBBI, AVR::INRdA) + .addReg(SCRATCH_REGISTER, RegState::Define) + .addImm(SREG_ADDR); + + // Disable exceptions. + buildMI(MBB, MBBI, AVR::BCLRs).addImm(7); // CLI + + f(MI); + + // Restore the status reg. + buildMI(MBB, MBBI, AVR::OUTARr) + .addImm(SREG_ADDR) + .addReg(SCRATCH_REGISTER); + + MI.eraseFromParent(); + return true; +} + +template +bool AVRExpandPseudo::expandAtomicBinaryOp(unsigned Opcode, Block &MBB, BlockIt MBBI, Func f) { + return expandAtomic(MBB, MBBI, [&](MachineInstr &MI) { + auto Op1 = MI.getOperand(0); + auto Op2 = MI.getOperand(1); + + MachineInstr &NewInst = *buildMI(MBB, MBBI, Opcode).addOperand(Op1).addOperand(Op2).getInstr(); + + f(NewInst); + }); +} + +bool AVRExpandPseudo::expandAtomicBinaryOp(unsigned Opcode, Block &MBB, BlockIt MBBI) { + return expandAtomicBinaryOp(Opcode, MBB, MBBI, [](MachineInstr &MI) {}); +} + +bool AVRExpandPseudo::expandAtomicArithmeticOp(unsigned Width, + unsigned ArithOpcode, + Block &MBB, + BlockIt MBBI) { + + return expandAtomic(MBB, MBBI, [&](MachineInstr &MI) { + auto Op1 = MI.getOperand(0); + auto Op2 = MI.getOperand(1); + + unsigned LoadOpcode = (Width == 8) ? AVR::LDRdPtr : AVR::LDWRdPtr; + unsigned StoreOpcode = (Width == 8) ? AVR::STPtrRr : AVR::STWPtrRr; + + // Create the load + buildMI(MBB, MBBI, LoadOpcode).addOperand(Op1).addOperand(Op2); + + // Create the arithmetic op + buildMI(MBB, MBBI, ArithOpcode).addOperand(Op1).addOperand(Op1).addOperand(Op2); + + // Create the store + buildMI(MBB, MBBI, StoreOpcode).addOperand(Op2).addOperand(Op1); + }); +} + +template<> +bool AVRExpandPseudo::expand(Block &MBB, BlockIt MBBI) { + return expandAtomicBinaryOp(AVR::LDRdPtr, MBB, MBBI); +} + +template<> +bool AVRExpandPseudo::expand(Block &MBB, BlockIt MBBI) { + return expandAtomicBinaryOp(AVR::LDWRdPtr, MBB, MBBI); +} + +template<> +bool AVRExpandPseudo::expand(Block &MBB, BlockIt MBBI) { + return expandAtomicBinaryOp(AVR::STPtrRr, MBB, MBBI); +} + +template<> +bool AVRExpandPseudo::expand(Block &MBB, BlockIt MBBI) { + return expandAtomicBinaryOp(AVR::STWPtrRr, MBB, MBBI); +} + +template<> +bool AVRExpandPseudo::expand(Block &MBB, BlockIt MBBI) { + return expandAtomicArithmeticOp(8, AVR::ADDRdRr, MBB, MBBI); +} + +template<> +bool AVRExpandPseudo::expand(Block &MBB, BlockIt MBBI) { + return expandAtomicArithmeticOp(16, AVR::ADDWRdRr, MBB, MBBI); +} + +template<> +bool AVRExpandPseudo::expand(Block &MBB, BlockIt MBBI) { + return expandAtomicArithmeticOp(8, AVR::SUBRdRr, MBB, MBBI); +} + +template<> +bool AVRExpandPseudo::expand(Block &MBB, BlockIt MBBI) { + return expandAtomicArithmeticOp(16, AVR::SUBWRdRr, MBB, MBBI); +} + +template<> +bool AVRExpandPseudo::expand(Block &MBB, BlockIt MBBI) { + return expandAtomicArithmeticOp(8, AVR::ANDRdRr, MBB, MBBI); +} + +template<> +bool AVRExpandPseudo::expand(Block &MBB, BlockIt MBBI) { + return expandAtomicArithmeticOp(16, AVR::ANDWRdRr, MBB, MBBI); +} + +template<> +bool AVRExpandPseudo::expand(Block &MBB, BlockIt MBBI) { + return expandAtomicArithmeticOp(8, AVR::ORRdRr, MBB, MBBI); +} + +template<> +bool AVRExpandPseudo::expand(Block &MBB, BlockIt MBBI) { + return expandAtomicArithmeticOp(16, AVR::ORWRdRr, MBB, MBBI); +} + +template<> +bool AVRExpandPseudo::expand(Block &MBB, BlockIt MBBI) { + return expandAtomicArithmeticOp(8, AVR::EORRdRr, MBB, MBBI); +} + +template<> +bool AVRExpandPseudo::expand(Block &MBB, BlockIt MBBI) { + return expandAtomicArithmeticOp(16, AVR::EORWRdRr, MBB, MBBI); +} + +template<> +bool AVRExpandPseudo::expand(Block &MBB, BlockIt MBBI) { + // On AVR, there is only one core and so atomic fences do nothing. + MBBI->eraseFromParent(); + return true; +} + template <> bool AVRExpandPseudo::expand(Block &MBB, BlockIt MBBI) { MachineInstr &MI = *MBBI; @@ -1305,7 +1479,7 @@ bool AVRExpandPseudo::expand(Block &MBB, BlockIt MBBI) { buildMI(MBB, MBBI, AVR::INRdA) .addReg(AVR::R0, RegState::Define) - .addImm(0x3f) + .addImm(SREG_ADDR) .setMIFlags(Flags); buildMI(MBB, MBBI, AVR::BCLRs).addImm(0x07).setMIFlags(Flags); @@ -1316,7 +1490,7 @@ bool AVRExpandPseudo::expand(Block &MBB, BlockIt MBBI) { .setMIFlags(Flags); buildMI(MBB, MBBI, AVR::OUTARr) - .addImm(0x3f) + .addImm(SREG_ADDR) .addReg(AVR::R0, RegState::Kill) .setMIFlags(Flags); @@ -1359,6 +1533,21 @@ bool AVRExpandPseudo::expandMI(Block &MBB, BlockIt MBBI) { EXPAND(AVR::LDWRdPtrPd); case AVR::LDDWRdYQ: //:FIXME: remove this once PR13375 gets fixed EXPAND(AVR::LDDWRdPtrQ); + EXPAND(AVR::AtomicLoad8); + EXPAND(AVR::AtomicLoad16); + EXPAND(AVR::AtomicStore8); + EXPAND(AVR::AtomicStore16); + EXPAND(AVR::AtomicLoadAdd8); + EXPAND(AVR::AtomicLoadAdd16); + EXPAND(AVR::AtomicLoadSub8); + EXPAND(AVR::AtomicLoadSub16); + EXPAND(AVR::AtomicLoadAnd8); + EXPAND(AVR::AtomicLoadAnd16); + EXPAND(AVR::AtomicLoadOr8); + EXPAND(AVR::AtomicLoadOr16); + EXPAND(AVR::AtomicLoadXor8); + EXPAND(AVR::AtomicLoadXor16); + EXPAND(AVR::AtomicFence); EXPAND(AVR::STSWKRr); EXPAND(AVR::STWPtrRr); EXPAND(AVR::STWPtrPiRr); diff --git a/lib/Target/AVR/AVRISelLowering.cpp b/lib/Target/AVR/AVRISelLowering.cpp index 80e279031fe..b45dcb4ad90 100644 --- a/lib/Target/AVR/AVRISelLowering.cpp +++ b/lib/Target/AVR/AVRISelLowering.cpp @@ -114,6 +114,17 @@ AVRTargetLowering::AVRTargetLowering(AVRTargetMachine &tm) setOperationAction(ISD::VAARG, MVT::Other, Expand); setOperationAction(ISD::VACOPY, MVT::Other, Expand); + // Atomic operations which must be lowered to rtlib calls + for (MVT VT : MVT::integer_valuetypes()) { + setOperationAction(ISD::ATOMIC_SWAP, VT, Expand); + setOperationAction(ISD::ATOMIC_CMP_SWAP, VT, Expand); + setOperationAction(ISD::ATOMIC_LOAD_NAND, VT, Expand); + setOperationAction(ISD::ATOMIC_LOAD_MAX, VT, Expand); + setOperationAction(ISD::ATOMIC_LOAD_MIN, VT, Expand); + setOperationAction(ISD::ATOMIC_LOAD_UMAX, VT, Expand); + setOperationAction(ISD::ATOMIC_LOAD_UMIN, VT, Expand); + } + // Division/remainder setOperationAction(ISD::UDIV, MVT::i8, Expand); setOperationAction(ISD::UDIV, MVT::i16, Expand); diff --git a/lib/Target/AVR/AVRInstrInfo.td b/lib/Target/AVR/AVRInstrInfo.td index ccf9fd1ba92..a479508020e 100644 --- a/lib/Target/AVR/AVRInstrInfo.td +++ b/lib/Target/AVR/AVRInstrInfo.td @@ -1237,6 +1237,38 @@ isReMaterializable = 1 in Requires<[HasSRAM]>; } +class AtomicLoad : + Pseudo<(outs DRC:$rd), (ins PTRREGS:$rr), "atomic_op", + [(set DRC:$rd, (Op i16:$rr))]>; + +class AtomicStore : + Pseudo<(outs), (ins LDSTPtrReg:$rd, DRC:$rr), "atomic_op", + [(Op i16:$rd, DRC:$rr)]>; + +class AtomicLoadOp : + Pseudo<(outs DRC:$rd), (ins PTRREGS:$rr, DRC:$operand), + "atomic_op", + [(set DRC:$rd, (Op i16:$rr, DRC:$operand))]>; + +def AtomicLoad8 : AtomicLoad; +def AtomicLoad16 : AtomicLoad; + +def AtomicStore8 : AtomicStore; +def AtomicStore16 : AtomicStore; + +def AtomicLoadAdd8 : AtomicLoadOp; +def AtomicLoadAdd16 : AtomicLoadOp; +def AtomicLoadSub8 : AtomicLoadOp; +def AtomicLoadSub16 : AtomicLoadOp; +def AtomicLoadAnd8 : AtomicLoadOp; +def AtomicLoadAnd16 : AtomicLoadOp; +def AtomicLoadOr8 : AtomicLoadOp; +def AtomicLoadOr16 : AtomicLoadOp; +def AtomicLoadXor8 : AtomicLoadOp; +def AtomicLoadXor16 : AtomicLoadOp; +def AtomicFence : Pseudo<(outs), (ins), "atomic_fence", + [(atomic_fence imm, imm)]>; + // Indirect store from register to data space. def STSKRr : F32DM<0b1, (outs), diff --git a/test/CodeGen/AVR/atomics/fence.ll b/test/CodeGen/AVR/atomics/fence.ll new file mode 100644 index 00000000000..6ea49bc7e3f --- /dev/null +++ b/test/CodeGen/AVR/atomics/fence.ll @@ -0,0 +1,13 @@ +; RUN: llc -mattr=avr6 < %s -march=avr | FileCheck %s + +; Checks that atomic fences are simply removed from IR. +; AVR is always singlethreaded so fences do nothing. + +; CHECK_LABEL: atomic_fence8 +; CHECK: ; BB#0: +; CHECK-NEXT: ret +define void @atomic_fence8() { + fence acquire + ret void +} + diff --git a/test/CodeGen/AVR/atomics/load16.ll b/test/CodeGen/AVR/atomics/load16.ll new file mode 100644 index 00000000000..ea021c0724b --- /dev/null +++ b/test/CodeGen/AVR/atomics/load16.ll @@ -0,0 +1,137 @@ +; RUN: llc -mattr=avr6 < %s -march=avr | FileCheck %s + +; CHECK-LABEL: atomic_load16 +; CHECK: in r0, 63 +; CHECK-NEXT: cli +; CHECK-NEXT: ld [[RR:r[0-9]+]], [[RD:(X|Y|Z)]] +; CHECK-NEXT: ldd [[RR:r[0-9]+]], [[RD:(X|Y|Z)]]+ +; CHECK-NEXT: out 63, r0 +define i16 @atomic_load16(i16* %foo) { + %val = load atomic i16, i16* %foo unordered, align 2 + ret i16 %val +} + +; CHECK-LABEL: atomic_load_swap16 +; CHECK: call __sync_lock_test_and_set_2 +define i16 @atomic_load_swap16(i16* %foo) { + %val = atomicrmw xchg i16* %foo, i16 13 seq_cst + ret i16 %val +} + +; CHECK-LABEL: atomic_load_cmp_swap16 +; CHECK: call __sync_val_compare_and_swap_2 +define i16 @atomic_load_cmp_swap16(i16* %foo) { + %val = cmpxchg i16* %foo, i16 5, i16 10 acq_rel monotonic + %value_loaded = extractvalue { i16, i1 } %val, 0 + ret i16 %value_loaded +} + +; CHECK-LABEL: atomic_load_add16 +; CHECK: in r0, 63 +; CHECK-NEXT: cli +; CHECK-NEXT: ld [[RR1:r[0-9]+]], [[RD1:(X|Y|Z)]] +; CHECK-NEXT: ldd [[RR2:r[0-9]+]], [[RD2:(X|Y|Z)]]+ +; CHECK-NEXT: add [[RR1]], [[TMP:r[0-9]+]] +; CHECK-NEXT: adc [[RR2]], [[TMP:r[0-9]+]] +; CHECK-NEXT: st [[RD1]], [[RR1]] +; CHECK-NEXT: std [[RD1]]+1, [[A:r[0-9]+]] +; CHECK-NEXT: out 63, r0 +define i16 @atomic_load_add16(i16* %foo) { + %val = atomicrmw add i16* %foo, i16 13 seq_cst + ret i16 %val +} + +; CHECK-LABEL: atomic_load_sub16 +; CHECK: in r0, 63 +; CHECK-NEXT: cli +; CHECK-NEXT: ld [[RR1:r[0-9]+]], [[RD1:(X|Y|Z)]] +; CHECK-NEXT: ldd [[RR2:r[0-9]+]], [[RD2:(X|Y|Z)]]+ +; CHECK-NEXT: sub [[RR1]], [[TMP:r[0-9]+]] +; CHECK-NEXT: sbc [[RR2]], [[TMP:r[0-9]+]] +; CHECK-NEXT: st [[RD1]], [[RR1]] +; CHECK-NEXT: std [[RD1]]+1, [[A:r[0-9]+]] +; CHECK-NEXT: out 63, r0 +define i16 @atomic_load_sub16(i16* %foo) { + %val = atomicrmw sub i16* %foo, i16 13 seq_cst + ret i16 %val +} + +; CHECK-LABEL: atomic_load_and16 +; CHECK: in r0, 63 +; CHECK-NEXT: cli +; CHECK-NEXT: ld [[RR1:r[0-9]+]], [[RD1:(X|Y|Z)]] +; CHECK-NEXT: ldd [[RR2:r[0-9]+]], [[RD2:(X|Y|Z)]]+ +; CHECK-NEXT: and [[RR1]], [[TMP:r[0-9]+]] +; CHECK-NEXT: and [[RR2]], [[TMP:r[0-9]+]] +; CHECK-NEXT: st [[RD1]], [[RR1]] +; CHECK-NEXT: std [[RD1]]+1, [[A:r[0-9]+]] +; CHECK-NEXT: out 63, r0 +define i16 @atomic_load_and16(i16* %foo) { + %val = atomicrmw and i16* %foo, i16 13 seq_cst + ret i16 %val +} + +; CHECK-LABEL: atomic_load_or16 +; CHECK: in r0, 63 +; CHECK-NEXT: cli +; CHECK-NEXT: ld [[RR1:r[0-9]+]], [[RD1:(X|Y|Z)]] +; CHECK-NEXT: ldd [[RR2:r[0-9]+]], [[RD2:(X|Y|Z)]]+ +; CHECK-NEXT: or [[RR1]], [[TMP:r[0-9]+]] +; CHECK-NEXT: or [[RR2]], [[TMP:r[0-9]+]] +; CHECK-NEXT: st [[RD1]], [[RR1]] +; CHECK-NEXT: std [[RD1]]+1, [[A:r[0-9]+]] +; CHECK-NEXT: out 63, r0 +define i16 @atomic_load_or16(i16* %foo) { + %val = atomicrmw or i16* %foo, i16 13 seq_cst + ret i16 %val +} + +; CHECK-LABEL: atomic_load_xor16 +; CHECK: in r0, 63 +; CHECK-NEXT: cli +; CHECK-NEXT: ld [[RR1:r[0-9]+]], [[RD1:(X|Y|Z)]] +; CHECK-NEXT: ldd [[RR2:r[0-9]+]], [[RD2:(X|Y|Z)]]+ +; CHECK-NEXT: eor [[RR1]], [[TMP:r[0-9]+]] +; CHECK-NEXT: eor [[RR2]], [[TMP:r[0-9]+]] +; CHECK-NEXT: st [[RD1]], [[RR1]] +; CHECK-NEXT: std [[RD1]]+1, [[A:r[0-9]+]] +; CHECK-NEXT: out 63, r0 +define i16 @atomic_load_xor16(i16* %foo) { + %val = atomicrmw xor i16* %foo, i16 13 seq_cst + ret i16 %val +} + +; CHECK-LABEL: atomic_load_nand16 +; CHECK: call __sync_fetch_and_nand_2 +define i16 @atomic_load_nand16(i16* %foo) { + %val = atomicrmw nand i16* %foo, i16 13 seq_cst + ret i16 %val +} + +; CHECK-LABEL: atomic_load_max16 +; CHECK: call __sync_fetch_and_max_2 +define i16 @atomic_load_max16(i16* %foo) { + %val = atomicrmw max i16* %foo, i16 13 seq_cst + ret i16 %val +} + +; CHECK-LABEL: atomic_load_min16 +; CHECK: call __sync_fetch_and_min_2 +define i16 @atomic_load_min16(i16* %foo) { + %val = atomicrmw min i16* %foo, i16 13 seq_cst + ret i16 %val +} + +; CHECK-LABEL: atomic_load_umax16 +; CHECK: call __sync_fetch_and_umax_2 +define i16 @atomic_load_umax16(i16* %foo) { + %val = atomicrmw umax i16* %foo, i16 13 seq_cst + ret i16 %val +} + +; CHECK-LABEL: atomic_load_umin16 +; CHECK: call __sync_fetch_and_umin_2 +define i16 @atomic_load_umin16(i16* %foo) { + %val = atomicrmw umin i16* %foo, i16 13 seq_cst + ret i16 %val +} diff --git a/test/CodeGen/AVR/atomics/load32.ll b/test/CodeGen/AVR/atomics/load32.ll new file mode 100644 index 00000000000..5320b2aeb1c --- /dev/null +++ b/test/CodeGen/AVR/atomics/load32.ll @@ -0,0 +1,16 @@ +; RUN: llc -mattr=avr6 < %s -march=avr | FileCheck %s + +; CHECK-LABEL: atomic_load32 +; CHECK: call __sync_val_compare_and_swap_4 +define i32 @atomic_load32(i32* %foo) { + %val = load atomic i32, i32* %foo unordered, align 4 + ret i32 %val +} + +; CHECK-LABEL: atomic_load_sub32 +; CHECK: call __sync_fetch_and_sub_4 +define i32 @atomic_load_sub32(i32* %foo) { + %val = atomicrmw sub i32* %foo, i32 13 seq_cst + ret i32 %val +} + diff --git a/test/CodeGen/AVR/atomics/load64.ll b/test/CodeGen/AVR/atomics/load64.ll new file mode 100644 index 00000000000..44360d57062 --- /dev/null +++ b/test/CodeGen/AVR/atomics/load64.ll @@ -0,0 +1,16 @@ +; RUN: llc -mattr=avr6 < %s -march=avr | FileCheck %s + +; CHECK-LABEL: atomic_load64 +; CHECK: call __sync_val_compare_and_swap_8 +define i64 @atomic_load64(i64* %foo) { + %val = load atomic i64, i64* %foo unordered, align 8 + ret i64 %val +} + +; CHECK-LABEL: atomic_load_sub64 +; CHECK: call __sync_fetch_and_sub_8 +define i64 @atomic_load_sub64(i64* %foo) { + %val = atomicrmw sub i64* %foo, i64 13 seq_cst + ret i64 %val +} + diff --git a/test/CodeGen/AVR/atomics/load8.ll b/test/CodeGen/AVR/atomics/load8.ll new file mode 100644 index 00000000000..a20e6626146 --- /dev/null +++ b/test/CodeGen/AVR/atomics/load8.ll @@ -0,0 +1,124 @@ +; RUN: llc -mattr=avr6 < %s -march=avr | FileCheck %s + +; Tests atomic operations on AVR + +; CHECK-LABEL: atomic_load8 +; CHECK: in r0, 63 +; CHECK-NEXT: cli +; CHECK-NEXT: ld [[RR:r[0-9]+]], [[RD:(X|Y|Z)]] +; CHECK-NEXT: out 63, r0 +define i8 @atomic_load8(i8* %foo) { + %val = load atomic i8, i8* %foo unordered, align 1 + ret i8 %val +} + +; CHECK-LABEL: atomic_load_swap8 +; CHECK: call __sync_lock_test_and_set_1 +define i8 @atomic_load_swap8(i8* %foo) { + %val = atomicrmw xchg i8* %foo, i8 13 seq_cst + ret i8 %val +} + +; CHECK-LABEL: atomic_load_cmp_swap8 +; CHECK: call __sync_val_compare_and_swap_1 +define i8 @atomic_load_cmp_swap8(i8* %foo) { + %val = cmpxchg i8* %foo, i8 5, i8 10 acq_rel monotonic + %value_loaded = extractvalue { i8, i1 } %val, 0 + ret i8 %value_loaded +} + +; CHECK-LABEL: atomic_load_add8 +; CHECK: in r0, 63 +; CHECK-NEXT: cli +; CHECK-NEXT: ld [[RD:r[0-9]+]], [[RR:(X|Y|Z)]] +; CHECK-NEXT: add [[RD]], [[RR1:r[0-9]+]] +; CHECK-NEXT: st [[RR]], [[RD]] +; CHECK-NEXT: out 63, r0 +define i8 @atomic_load_add8(i8* %foo) { + %val = atomicrmw add i8* %foo, i8 13 seq_cst + ret i8 %val +} + +; CHECK-LABEL: atomic_load_sub8 +; CHECK: in r0, 63 +; CHECK-NEXT: cli +; CHECK-NEXT: ld [[RD:r[0-9]+]], [[RR:(X|Y|Z)]] +; CHECK-NEXT: sub [[RD]], [[RR1:r[0-9]+]] +; CHECK-NEXT: st [[RR]], [[RD]] +; CHECK-NEXT: out 63, r0 +define i8 @atomic_load_sub8(i8* %foo) { + %val = atomicrmw sub i8* %foo, i8 13 seq_cst + ret i8 %val +} + +; CHECK-LABEL: atomic_load_and8 +; CHECK: in r0, 63 +; CHECK-NEXT: cli +; CHECK-NEXT: ld [[RD:r[0-9]+]], [[RR:(X|Y|Z)]] +; CHECK-NEXT: and [[RD]], [[RR1:r[0-9]+]] +; CHECK-NEXT: st [[RR]], [[RD]] +; CHECK-NEXT: out 63, r0 +define i8 @atomic_load_and8(i8* %foo) { + %val = atomicrmw and i8* %foo, i8 13 seq_cst + ret i8 %val +} + +; CHECK-LABEL: atomic_load_or8 +; CHECK: in r0, 63 +; CHECK-NEXT: cli +; CHECK-NEXT: ld [[RD:r[0-9]+]], [[RR:(X|Y|Z)]] +; CHECK-NEXT: or [[RD]], [[RR1:r[0-9]+]] +; CHECK-NEXT: st [[RR]], [[RD]] +; CHECK-NEXT: out 63, r0 +define i8 @atomic_load_or8(i8* %foo) { + %val = atomicrmw or i8* %foo, i8 13 seq_cst + ret i8 %val +} + +; CHECK-LABEL: atomic_load_xor8 +; CHECK: in r0, 63 +; CHECK-NEXT: cli +; CHECK-NEXT: ld [[RD:r[0-9]+]], [[RR:(X|Y|Z)]] +; CHECK-NEXT: eor [[RD]], [[RR1:r[0-9]+]] +; CHECK-NEXT: st [[RR]], [[RD]] +; CHECK-NEXT: out 63, r0 +define i8 @atomic_load_xor8(i8* %foo) { + %val = atomicrmw xor i8* %foo, i8 13 seq_cst + ret i8 %val +} + +; CHECK-LABEL: atomic_load_nand8 +; CHECK: call __sync_fetch_and_nand_1 +define i8 @atomic_load_nand8(i8* %foo) { + %val = atomicrmw nand i8* %foo, i8 13 seq_cst + ret i8 %val +} + +; CHECK-LABEL: atomic_load_max8 +; CHECK: call __sync_fetch_and_max_1 +define i8 @atomic_load_max8(i8* %foo) { + %val = atomicrmw max i8* %foo, i8 13 seq_cst + ret i8 %val +} + +; CHECK-LABEL: atomic_load_min8 +; CHECK: call __sync_fetch_and_min_1 +define i8 @atomic_load_min8(i8* %foo) { + %val = atomicrmw min i8* %foo, i8 13 seq_cst + ret i8 %val +} + +; CHECK-LABEL: atomic_load_umax8 +; CHECK: call __sync_fetch_and_umax_1 +define i8 @atomic_load_umax8(i8* %foo) { + %val = atomicrmw umax i8* %foo, i8 13 seq_cst + ret i8 %val +} + +; CHECK-LABEL: atomic_load_umin8 +; CHECK: call __sync_fetch_and_umin_1 +define i8 @atomic_load_umin8(i8* %foo) { + %val = atomicrmw umin i8* %foo, i8 13 seq_cst + ret i8 %val +} + diff --git a/test/CodeGen/AVR/atomics/store.ll b/test/CodeGen/AVR/atomics/store.ll new file mode 100644 index 00000000000..e1231c21e7d --- /dev/null +++ b/test/CodeGen/AVR/atomics/store.ll @@ -0,0 +1,37 @@ +; RUN: llc -mattr=avr6 < %s -march=avr | FileCheck %s + +; CHECK-LABEL: atomic_store8 +; CHECK: in r0, 63 +; CHECK-NEXT: cli +; CHECK-NEXT: st [[RD:(X|Y|Z)]], [[RR:r[0-9]+]] +; CHECK-NEXT: out 63, r0 +define void @atomic_store8(i8* %foo) { + store atomic i8 1, i8* %foo unordered, align 1 + ret void +} + +; CHECK-LABEL: atomic_store16 +; CHECK: in r0, 63 +; CHECK-NEXT: cli +; CHECK-NEXT: st [[RD:(X|Y|Z)]], [[RR:r[0-9]+]] +; CHECK-NEXT: std [[RD]]+1, [[RR:r[0-9]+]] +; CHECK-NEXT: out 63, r0 +define void @atomic_store16(i16* %foo) { + store atomic i16 1, i16* %foo unordered, align 2 + ret void +} + +; CHECK-LABEL: atomic_store32 +; CHECK: call __sync_lock_test_and_set_4 +define void @atomic_store32(i32* %foo) { + store atomic i32 1, i32* %foo unordered, align 4 + ret void +} + +; CHECK-LABEL: atomic_store64 +; CHECK: call __sync_lock_test_and_set_8 +define void @atomic_store64(i64* %foo) { + store atomic i64 1, i64* %foo unordered, align 8 + ret void +} + diff --git a/test/CodeGen/AVR/atomics/store16.ll b/test/CodeGen/AVR/atomics/store16.ll new file mode 100644 index 00000000000..f5738d83d69 --- /dev/null +++ b/test/CodeGen/AVR/atomics/store16.ll @@ -0,0 +1,13 @@ +; RUN: llc -mattr=avr6 < %s -march=avr | FileCheck %s + +; CHECK-LABEL: atomic_store16 +; CHECK: in r0, 63 +; CHECK-NEXT: cli +; CHECK-NEXT: st [[RD:(X|Y|Z)]], [[RR:r[0-9]+]] +; CHECK-NEXT: std [[RD:(X|Y|Z)]]+1, [[RR:r[0-9]+]] +; CHECK-NEXT: out 63, r0 +define void @atomic_store16(i16* %foo) { + store atomic i16 1, i16* %foo unordered, align 2 + ret void +} + diff --git a/test/CodeGen/AVR/atomics/swap.ll b/test/CodeGen/AVR/atomics/swap.ll new file mode 100644 index 00000000000..69372643c66 --- /dev/null +++ b/test/CodeGen/AVR/atomics/swap.ll @@ -0,0 +1,30 @@ +; RUN: llc -mattr=avr6 < %s -march=avr | FileCheck %s + +; CHECK-LABEL: atomic_swap8 +; CHECK: call __sync_lock_test_and_set_1 +define i8 @atomic_swap8(i8* %foo) { + %val = atomicrmw xchg i8* %foo, i8 13 seq_cst + ret i8 %val +} + +; CHECK-LABEL: atomic_swap16 +; CHECK: call __sync_lock_test_and_set_2 +define i16 @atomic_swap16(i16* %foo) { + %val = atomicrmw xchg i16* %foo, i16 13 seq_cst + ret i16 %val +} + +; CHECK-LABEL: atomic_swap32 +; CHECK: call __sync_lock_test_and_set_4 +define i32 @atomic_swap32(i32* %foo) { + %val = atomicrmw xchg i32* %foo, i32 13 seq_cst + ret i32 %val +} + +; CHECK-LABEL: atomic_swap64 +; CHECK: call __sync_lock_test_and_set_8 +define i64 @atomic_swap64(i64* %foo) { + %val = atomicrmw xchg i64* %foo, i64 13 seq_cst + ret i64 %val +} +