diff --git a/clang/include/clang/Basic/BuiltinsAArch64.def b/clang/include/clang/Basic/BuiltinsAArch64.def --- a/clang/include/clang/Basic/BuiltinsAArch64.def +++ b/clang/include/clang/Basic/BuiltinsAArch64.def @@ -89,6 +89,12 @@ // Misc BUILTIN(__builtin_sponentry, "v*", "c") +// Transactional Memory Extension +BUILTIN(__builtin_arm_tstart, "WUi", "nj") +BUILTIN(__builtin_arm_tcommit, "v", "n") +BUILTIN(__builtin_arm_tcancel, "vWUIi", "nr") +BUILTIN(__builtin_arm_ttest, "WUi", "nc") + TARGET_HEADER_BUILTIN(_BitScanForward, "UcUNi*UNi", "nh", "intrin.h", ALL_MS_LANGUAGES, "") TARGET_HEADER_BUILTIN(_BitScanReverse, "UcUNi*UNi", "nh", "intrin.h", ALL_MS_LANGUAGES, "") TARGET_HEADER_BUILTIN(_BitScanForward64, "UcUNi*ULLi", "nh", "intrin.h", ALL_MS_LANGUAGES, "") diff --git a/clang/lib/Basic/Targets/AArch64.h b/clang/lib/Basic/Targets/AArch64.h --- a/clang/lib/Basic/Targets/AArch64.h +++ b/clang/lib/Basic/Targets/AArch64.h @@ -28,13 +28,15 @@ enum FPUModeEnum { FPUMode, NeonMode = (1 << 0), SveMode = (1 << 1) }; unsigned FPU; - unsigned CRC; - unsigned Crypto; - unsigned Unaligned; - unsigned HasFullFP16; - unsigned HasDotProd; - unsigned HasFP16FML; - unsigned HasMTE; + bool HasCRC; + bool HasCrypto; + bool HasUnaligned; + bool HasFullFP16; + bool HasDotProd; + bool HasFP16FML; + bool HasMTE; + bool HasTME; + llvm::AArch64::ArchKind ArchKind; static const Builtin::Info BuiltinInfo[]; diff --git a/clang/lib/Basic/Targets/AArch64.cpp b/clang/lib/Basic/Targets/AArch64.cpp --- a/clang/lib/Basic/Targets/AArch64.cpp +++ b/clang/lib/Basic/Targets/AArch64.cpp @@ -177,13 +177,13 @@ if (FPU & SveMode) Builder.defineMacro("__ARM_FEATURE_SVE", "1"); - if (CRC) + if (HasCRC) Builder.defineMacro("__ARM_FEATURE_CRC32", "1"); - if (Crypto) + if (HasCrypto) Builder.defineMacro("__ARM_FEATURE_CRYPTO", "1"); - if (Unaligned) + if (HasUnaligned) Builder.defineMacro("__ARM_FEATURE_UNALIGNED", "1"); if ((FPU & NeonMode) && HasFullFP16) @@ -197,6 +197,9 @@ if (HasMTE) Builder.defineMacro("__ARM_FEATURE_MEMORY_TAGGING", "1"); + if (HasTME) + Builder.defineMacro("__ARM_FEATURE_TME", "1"); + if ((FPU & NeonMode) && HasFP16FML) Builder.defineMacro("__ARM_FEATURE_FP16FML", "1"); @@ -232,13 +235,14 @@ bool AArch64TargetInfo::handleTargetFeatures(std::vector &Features, DiagnosticsEngine &Diags) { FPU = FPUMode; - CRC = 0; - Crypto = 0; - Unaligned = 1; - HasFullFP16 = 0; - HasDotProd = 0; - HasFP16FML = 0; - HasMTE = 0; + HasCRC = false; + HasCrypto = false; + HasUnaligned = true; + HasFullFP16 = false; + HasDotProd = false; + HasFP16FML = false; + HasMTE = false; + HasTME = false; ArchKind = llvm::AArch64::ArchKind::ARMV8A; for (const auto &Feature : Features) { @@ -247,23 +251,25 @@ if (Feature == "+sve") FPU |= SveMode; if (Feature == "+crc") - CRC = 1; + HasCRC = true; if (Feature == "+crypto") - Crypto = 1; + HasCrypto = true; if (Feature == "+strict-align") - Unaligned = 0; + HasUnaligned = false; if (Feature == "+v8.1a") ArchKind = llvm::AArch64::ArchKind::ARMV8_1A; if (Feature == "+v8.2a") ArchKind = llvm::AArch64::ArchKind::ARMV8_2A; if (Feature == "+fullfp16") - HasFullFP16 = 1; + HasFullFP16 = true; if (Feature == "+dotprod") - HasDotProd = 1; + HasDotProd = true; if (Feature == "+fp16fml") - HasFP16FML = 1; + HasFP16FML = true; if (Feature == "+mte") - HasMTE = 1; + HasMTE = true; + if (Feature == "+tme") + HasTME = true; } setDataLayout(); diff --git a/clang/lib/CodeGen/CGBuiltin.cpp b/clang/lib/CodeGen/CGBuiltin.cpp --- a/clang/lib/CodeGen/CGBuiltin.cpp +++ b/clang/lib/CodeGen/CGBuiltin.cpp @@ -7220,6 +7220,19 @@ } } + /* Transactional Memory Extentions (TME) intrinsics. Check range of __tcancel + argument, other intrinsics are handled automatically. */ + if (BuiltinID == AArch64::BI__builtin_arm_tcancel) { + llvm::APSInt Arg; + bool IsConst= E->getArg(0)->isIntegerConstantExpr(Arg, getContext()); + assert(IsConst && "Constant arg isn't actually constant?"); + (void)IsConst; + if (!(Arg <= 0xffffu)) + CGM.Error(E->getArg(0)->getExprLoc(), "argument must be in range [0, 65535]"); + auto ArgValue = llvm::ConstantInt::get(getLLVMContext(), Arg); + return Builder.CreateCall(CGM.getIntrinsic(Intrinsic::aarch64_tcancel), {ArgValue}); + } + if (BuiltinID == AArch64::BI__builtin_arm_rsr || BuiltinID == AArch64::BI__builtin_arm_rsr64 || BuiltinID == AArch64::BI__builtin_arm_rsrp || diff --git a/clang/lib/Headers/arm_acle.h b/clang/lib/Headers/arm_acle.h --- a/clang/lib/Headers/arm_acle.h +++ b/clang/lib/Headers/arm_acle.h @@ -605,7 +605,7 @@ #define __arm_wsr64(sysreg, v) __builtin_arm_wsr64(sysreg, v) #define __arm_wsrp(sysreg, v) __builtin_arm_wsrp(sysreg, v) -// Memory Tagging Extensions (MTE) Intrinsics +/* Memory Tagging Extensions (MTE) Intrinsics */ #if __ARM_FEATURE_MEMORY_TAGGING #define __arm_mte_create_random_tag(__ptr, __mask) __builtin_arm_irg(__ptr, __mask) #define __arm_mte_increment_tag(__ptr, __tag_offset) __builtin_arm_addg(__ptr, __tag_offset) @@ -615,6 +615,28 @@ #define __arm_mte_ptrdiff(__ptra, __ptrb) __builtin_arm_subp(__ptra, __ptrb) #endif +/* Transactional Memory Extension (TME) Intrinsics */ +#if __ARM_FEATURE_TME + +#define _TMFAILURE_REASON 0x00007fffu +#define _TMFAILURE_RTRY 0x00008000u +#define _TMFAILURE_CNCL 0x00010000u +#define _TMFAILURE_MEM 0x00020000u +#define _TMFAILURE_IMP 0x00040000u +#define _TMFAILURE_ERR 0x00080000u +#define _TMFAILURE_SIZE 0x00100000u +#define _TMFAILURE_NEST 0x00200000u +#define _TMFAILURE_DBG 0x00400000u +#define _TMFAILURE_INT 0x00800000u +#define _TMFAILURE_TRIVIAL 0x01000000u + +#define __tstart() __builtin_arm_tstart() +#define __tcommit() __builtin_arm_tcommit() +#define __tcancel(__arg) __builtin_arm_tcancel(__arg) +#define __ttest() __builtin_arm_ttest() + +#endif /* __ARM_FEATURE_TME */ + #if defined(__cplusplus) } #endif diff --git a/clang/test/CodeGen/aarch64-tme-errors.c b/clang/test/CodeGen/aarch64-tme-errors.c new file mode 100644 --- /dev/null +++ b/clang/test/CodeGen/aarch64-tme-errors.c @@ -0,0 +1,11 @@ +// RUN: not %clang_cc1 -triple aarch64-eabi -fsyntax-only %s -o - 2>&1 | FileCheck %s + +#include "arm_acle.h" + +void test_no_tme_funcs() { + __tstart(); + __builtin_tstart(); +} + +// CHECK: implicit declaration of function '__tstart' +// CHECK: use of unknown builtin '__builtin_tstart' diff --git a/clang/test/CodeGen/aarch64-tme-tcancel-arg.cpp b/clang/test/CodeGen/aarch64-tme-tcancel-arg.cpp new file mode 100644 --- /dev/null +++ b/clang/test/CodeGen/aarch64-tme-tcancel-arg.cpp @@ -0,0 +1,10 @@ +// RUN: %clang_cc1 -triple aarch64-eabi -target-feature +tme -S -emit-llvm %s -o - | FileCheck %s + +#define A -1 +constexpr int f() { return 65536; } + +void t_cancel() { + __builtin_arm_tcancel(f() + A); +} + +// CHECK: call void @llvm.aarch64.tcancel(i64 65535) diff --git a/clang/test/CodeGen/aarch64-tme-tcancel-const-error.c b/clang/test/CodeGen/aarch64-tme-tcancel-const-error.c new file mode 100644 --- /dev/null +++ b/clang/test/CodeGen/aarch64-tme-tcancel-const-error.c @@ -0,0 +1,6 @@ +// RUN: not %clang_cc1 -triple aarch64-eabi -target-feature +tme -S -emit-llvm %s -o - 2>&1 | FileCheck %s +void t_cancel(unsigned short u) { + __builtin_arm_tcancel(u); +} + +// CHECK: error: argument to '__builtin_arm_tcancel' must be a constant integer diff --git a/clang/test/CodeGen/aarch64-tme-tcancel-range-error.c b/clang/test/CodeGen/aarch64-tme-tcancel-range-error.c new file mode 100644 --- /dev/null +++ b/clang/test/CodeGen/aarch64-tme-tcancel-range-error.c @@ -0,0 +1,5 @@ +// RUN: not %clang_cc1 -triple aarch64-eabi -target-feature +tme -S -emit-llvm %s -o - 2>&1 | FileCheck %s +void t_cancel() { + __builtin_arm_tcancel(0x12345u); +} +// CHECK: argument must be in range [0, 65535] diff --git a/clang/test/CodeGen/aarch64-tme.c b/clang/test/CodeGen/aarch64-tme.c new file mode 100644 --- /dev/null +++ b/clang/test/CodeGen/aarch64-tme.c @@ -0,0 +1,37 @@ +// RUN: %clang_cc1 -triple aarch64-eabi -target-feature +tme -S -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -DUSE_ACLE -triple aarch64-eabi -target-feature +tme -S -emit-llvm %s -o - | FileCheck %s + +#ifdef USE_ACLE +#include "arm_acle.h" +void test_tme_funcs() { + __tstart(); + (void)__ttest(); + __tcommit(); + __tcancel(0x789a); +} +#else +void test_tme_funcs() { + __builtin_arm_tstart(); + (void)__builtin_arm_ttest(); + __builtin_arm_tcommit(); + __builtin_arm_tcancel(0x789a); +} +#endif +// CHECK: call i64 @llvm.aarch64.tstart() +// CHECK: call i64 @llvm.aarch64.ttest() +// CHECK: call void @llvm.aarch64.tcommit() +// CHECK: call void @llvm.aarch64.tcancel(i64 30874) + +// CHECK: declare i64 @llvm.aarch64.tstart() #1 +// CHECK: declare i64 @llvm.aarch64.ttest() #2 +// CHECK: declare void @llvm.aarch64.tcommit() #1 +// CHECK: declare void @llvm.aarch64.tcancel(i64 immarg) #3 + +#ifdef __ARM_FEATURE_TME +void arm_feature_tme_defined() {} +#endif +// CHECK: define void @arm_feature_tme_defined() + +// CHECK: attributes #1 = { nounwind } +// CHECK: attributes #2 = { nounwind readnone } +// CHECK: attributes #3 = { noreturn } diff --git a/llvm/include/llvm/IR/IntrinsicsAArch64.td b/llvm/include/llvm/IR/IntrinsicsAArch64.td --- a/llvm/include/llvm/IR/IntrinsicsAArch64.td +++ b/llvm/include/llvm/IR/IntrinsicsAArch64.td @@ -701,3 +701,17 @@ def int_aarch64_subp : Intrinsic<[llvm_i64_ty], [llvm_ptr_ty, llvm_ptr_ty], [IntrNoMem]>; } + +// Transactional Memory Extension (TME) Intrinsics +let TargetPrefix = "aarch64" in { +def int_aarch64_tstart : GCCBuiltin<"__builtin_arm_tstart">, + Intrinsic<[llvm_i64_ty], [], [IntrNoMem, IntrHasSideEffects]>; + +def int_aarch64_tcommit : GCCBuiltin<"__builtin_arm_tcommit">, Intrinsic<[], [], [IntrNoMem, IntrHasSideEffects] >; + +def int_aarch64_tcancel : Intrinsic<[], [llvm_i64_ty], + [ImmArg<0>, IntrNoMem, IntrHasSideEffects, IntrNoReturn, Throws]>; + +def int_aarch64_ttest : GCCBuiltin<"__builtin_arm_ttest">, + Intrinsic<[llvm_i64_ty], [], [IntrNoMem]>; +} diff --git a/llvm/include/llvm/Support/AArch64TargetParser.h b/llvm/include/llvm/Support/AArch64TargetParser.h --- a/llvm/include/llvm/Support/AArch64TargetParser.h +++ b/llvm/include/llvm/Support/AArch64TargetParser.h @@ -54,6 +54,7 @@ AEK_SVE2SM4 = 1 << 25, AEK_SVE2SHA3 = 1 << 26, AEK_BITPERM = 1 << 27, + AEK_TME = 1 << 28, }; enum class ArchKind { diff --git a/llvm/include/llvm/Support/AArch64TargetParser.def b/llvm/include/llvm/Support/AArch64TargetParser.def --- a/llvm/include/llvm/Support/AArch64TargetParser.def +++ b/llvm/include/llvm/Support/AArch64TargetParser.def @@ -79,6 +79,7 @@ AARCH64_ARCH_EXT_NAME("ssbs", AArch64::AEK_SSBS, "+ssbs", "-ssbs") AARCH64_ARCH_EXT_NAME("sb", AArch64::AEK_SB, "+sb", "-sb") AARCH64_ARCH_EXT_NAME("predres", AArch64::AEK_PREDRES, "+predres", "-predres") +AARCH64_ARCH_EXT_NAME("tme", AArch64::AEK_TME, "+tme", "-tme") #undef AARCH64_ARCH_EXT_NAME #ifndef AARCH64_CPU_NAME diff --git a/llvm/lib/Target/AArch64/AArch64.td b/llvm/lib/Target/AArch64/AArch64.td --- a/llvm/lib/Target/AArch64/AArch64.td +++ b/llvm/lib/Target/AArch64/AArch64.td @@ -345,6 +345,9 @@ def FeatureMTE : SubtargetFeature<"mte", "HasMTE", "true", "Enable Memory Tagging Extension" >; +def FeatureTME : SubtargetFeature<"tme", "HasTME", + "true", "Enable Transactional Memory Extension" >; + //===----------------------------------------------------------------------===// // Architectures. // diff --git a/llvm/lib/Target/AArch64/AArch64InstrFormats.td b/llvm/lib/Target/AArch64/AArch64InstrFormats.td --- a/llvm/lib/Target/AArch64/AArch64InstrFormats.td +++ b/llvm/lib/Target/AArch64/AArch64InstrFormats.td @@ -714,12 +714,15 @@ let ParserMatchClass = LogicalImm64NotOperand; } -// imm0_65535 predicate - True if the immediate is in the range [0,65535]. -def imm0_65535 : Operand, ImmLeaf, PrintMethod = "printImmHex" in { +def i32_imm0_65535 : Operand, ImmLeaf { - let ParserMatchClass = AsmImmRange<0, 65535>; - let PrintMethod = "printImmHex"; +}]>; + +def i64_imm0_65535 : Operand, ImmLeaf; } // imm0_255 predicate - True if the immediate is in the range [0,255]. @@ -1054,6 +1057,19 @@ } +// Transactional Memory Extension (TME) Instructions +class TMEInst pattern = []> + : I, + Sched<[WriteSys]> { + let Inst{31-0} = 0b11010101000000110011000001111111; + let DecoderMethod = ""; + + let mayLoad = 0; + let mayStore = 0; + let hasSideEffects = 1; +} + + //--- // System management //--- @@ -4086,7 +4102,7 @@ let mayLoad = 0, mayStore = 0, hasSideEffects = 1 in class ExceptionGeneration op1, bits<2> ll, string asm> - : I<(outs), (ins imm0_65535:$imm), asm, "\t$imm", "", []>, + : I<(outs), (ins i32_imm0_65535:$imm), asm, "\t$imm", "", []>, Sched<[WriteSys]> { bits<16> imm; let Inst{31-24} = 0b11010100; diff --git a/llvm/lib/Target/AArch64/AArch64InstrInfo.td b/llvm/lib/Target/AArch64/AArch64InstrInfo.td --- a/llvm/lib/Target/AArch64/AArch64InstrInfo.td +++ b/llvm/lib/Target/AArch64/AArch64InstrInfo.td @@ -133,6 +133,8 @@ AssemblerPredicate<"FeatureBranchTargetId", "bti">; def HasMTE : Predicate<"Subtarget->hasMTE()">, AssemblerPredicate<"FeatureMTE", "mte">; +def HasTME : Predicate<"Subtarget->hasTME()">, + AssemblerPredicate<"FeatureTME", "tme">; def IsLE : Predicate<"Subtarget->isLittleEndian()">; def IsBE : Predicate<"!Subtarget->isLittleEndian()">; def IsWindows : Predicate<"Subtarget->isTargetWindows()">; @@ -796,6 +798,36 @@ (SYSxt imm0_7:$op1, sys_cr_op:$Cn, sys_cr_op:$Cm, imm0_7:$op2, XZR)>; +// Transactional Memory Extension instructions +let Predicates = [HasTME] in { + +def TSTART : TMEInst<(outs GPR64:$Rt), (ins), "tstart", "\t$Rt", [(set GPR64:$Rt, (int_aarch64_tstart))]> { + bits<5> Rt; + let Inst{21} = 1; + let Inst{4-0} = Rt; +} + +def TTEST : TMEInst<(outs GPR64:$Rt), (ins), "ttest", "\t$Rt", [(set GPR64:$Rt, (int_aarch64_ttest))]> { + bits<5> Rt; + let Inst{21} = 1; + let Inst{8} = 1; + let Inst{4-0} = Rt; + + let hasSideEffects = 0; +} + +def TCANCEL : TMEInst<(outs), (ins i64_imm0_65535:$imm), "tcancel", "\t$imm", [(int_aarch64_tcancel i64_imm0_65535:$imm)]> { + bits<16> imm; + let Inst{24-21} = 0b0011; + let Inst{20-5} = imm; + let Inst{4-0} = 0b00000; + + let isBarrier = 1; +} + +def TCOMMIT : TMEInst<(outs), (ins), "tcommit", "", [(int_aarch64_tcommit)]>; +} + //===----------------------------------------------------------------------===// // Move immediate instructions. //===----------------------------------------------------------------------===// @@ -807,12 +839,12 @@ defm MOVZ : MoveImmediate<0b10, "movz">; // First group of aliases covers an implicit "lsl #0". -def : InstAlias<"movk $dst, $imm", (MOVKWi GPR32:$dst, imm0_65535:$imm, 0), 0>; -def : InstAlias<"movk $dst, $imm", (MOVKXi GPR64:$dst, imm0_65535:$imm, 0), 0>; -def : InstAlias<"movn $dst, $imm", (MOVNWi GPR32:$dst, imm0_65535:$imm, 0)>; -def : InstAlias<"movn $dst, $imm", (MOVNXi GPR64:$dst, imm0_65535:$imm, 0)>; -def : InstAlias<"movz $dst, $imm", (MOVZWi GPR32:$dst, imm0_65535:$imm, 0)>; -def : InstAlias<"movz $dst, $imm", (MOVZXi GPR64:$dst, imm0_65535:$imm, 0)>; +def : InstAlias<"movk $dst, $imm", (MOVKWi GPR32:$dst, i32_imm0_65535:$imm, 0), 0>; +def : InstAlias<"movk $dst, $imm", (MOVKXi GPR64:$dst, i32_imm0_65535:$imm, 0), 0>; +def : InstAlias<"movn $dst, $imm", (MOVNWi GPR32:$dst, i32_imm0_65535:$imm, 0)>; +def : InstAlias<"movn $dst, $imm", (MOVNXi GPR64:$dst, i32_imm0_65535:$imm, 0)>; +def : InstAlias<"movz $dst, $imm", (MOVZWi GPR32:$dst, i32_imm0_65535:$imm, 0)>; +def : InstAlias<"movz $dst, $imm", (MOVZXi GPR64:$dst, i32_imm0_65535:$imm, 0)>; // Next, we have various ELF relocations with the ":XYZ_g0:sym" syntax. def : InstAlias<"movz $Rd, $sym", (MOVZXi GPR64:$Rd, movz_symbol_g3:$sym, 48)>; diff --git a/llvm/lib/Target/AArch64/AArch64Subtarget.h b/llvm/lib/Target/AArch64/AArch64Subtarget.h --- a/llvm/lib/Target/AArch64/AArch64Subtarget.h +++ b/llvm/lib/Target/AArch64/AArch64Subtarget.h @@ -134,6 +134,7 @@ bool HasBTI = false; bool HasRandGen = false; bool HasMTE = false; + bool HasTME = false; // Arm SVE2 extensions bool HasSVE2AES = false; @@ -380,6 +381,7 @@ bool hasBTI() const { return HasBTI; } bool hasRandGen() const { return HasRandGen; } bool hasMTE() const { return HasMTE; } + bool hasTME() const { return HasTME; } // Arm SVE2 extensions bool hasSVE2AES() const { return HasSVE2AES; } bool hasSVE2SM4() const { return HasSVE2SM4; } diff --git a/llvm/test/CodeGen/AArch64/tme.ll b/llvm/test/CodeGen/AArch64/tme.ll new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/AArch64/tme.ll @@ -0,0 +1,99 @@ +; RUN: llc %s -o - | FileCheck %s + +; #include "arm_acle.h" +; +; _Bool cas(int *p, int old, int new) { +; uint64_t s; +; while ((s = __tstart()) != 0) { +; if ((s & _TMFAILURE_RTRY) == 0) +; return 0; +; } +; +; if (__ttest() != 1) +; return 0; +; +; if (*p != old) +; __tcancel(0); +; +; *p = new; +; __tcommit(); +; return 1; +; } + +; ModuleID = 'cas.c' +source_filename = "cas.c" +target datalayout = "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128" +target triple = "aarch64-unknown-unknown-eabi" + +; Function Attrs: nounwind +define dso_local i1 @cas(i32* nocapture %p, i32 %old, i32 %new) local_unnamed_addr #0 { +entry: + br label %while.cond + +while.cond: ; preds = %while.body, %entry + %0 = tail call i64 @llvm.aarch64.tstart() + %cmp = icmp eq i64 %0, 0 + br i1 %cmp, label %while.end, label %while.body + +while.body: ; preds = %while.cond + %and = and i64 %0, 32768 + %cmp1 = icmp eq i64 %and, 0 + br i1 %cmp1, label %cleanup, label %while.cond + +while.end: ; preds = %while.cond + %1 = tail call i64 @llvm.aarch64.ttest() + %cmp2 = icmp eq i64 %1, 1 + br i1 %cmp2, label %if.end4, label %cleanup + +if.end4: ; preds = %while.end + %2 = load i32, i32* %p, align 4, !tbaa !2 + %cmp5 = icmp eq i32 %2, %old + br i1 %cmp5, label %if.end7, label %if.then6 + +if.then6: ; preds = %if.end4 + tail call void @llvm.aarch64.tcancel(i64 0) #1 + unreachable + +if.end7: ; preds = %if.end4 + store i32 %new, i32* %p, align 4, !tbaa !2 + tail call void @llvm.aarch64.tcommit() + br label %cleanup + +cleanup: ; preds = %while.body, %while.end, %if.end7 + %retval.0 = phi i1 [ true, %if.end7 ], [ false, %while.end ], [ false, %while.body ] + ret i1 %retval.0 +} + +; Function Attrs: nounwind +declare i64 @llvm.aarch64.tstart() #1 + +; Function Attrs: nounwind readnone +declare i64 @llvm.aarch64.ttest() #2 + +; Function Attrs: noreturn +declare void @llvm.aarch64.tcancel(i64 immarg) #3 + +; Function Attrs: nounwind +declare void @llvm.aarch64.tcommit() #1 + +attributes #0 = { nounwind "correctly-rounded-divide-sqrt-fp-math"="false" "disable-tail-calls"="false" "less-precise-fpmad"="false" "min-legal-vector-width"="0" "no-frame-pointer-elim"="true" "no-frame-pointer-elim-non-leaf" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="generic" "target-features"="+neon,+tme" "unsafe-fp-math"="false" "use-soft-float"="false" } +attributes #1 = { nounwind } +attributes #2 = { nounwind readnone } +attributes #3 = { noreturn } + +!llvm.module.flags = !{!0} +!llvm.ident = !{!1} + +!0 = !{i32 1, !"wchar_size", i32 4} +!1 = !{!"clang version 9.0.0 (https://github.com/llvm/llvm-project.git 910d27fbb1b1faef9f2fa6dedddb1edc6b12ebf3)"} +!2 = !{!3, !3, i64 0} +!3 = !{!"int", !4, i64 0} +!4 = !{!"omnipotent char", !5, i64 0} +!5 = !{!"Simple C/C++ TBAA"} + +; CHECK-LABEL: cas +; CHECK: tstart x +; CHECK: ttest x +; CHECK: tcommit +; CHECK: tcancel #0 +; CHECK-NEXT: .Lfunc_end0 diff --git a/llvm/test/MC/AArch64/tme-error.s b/llvm/test/MC/AArch64/tme-error.s new file mode 100644 --- /dev/null +++ b/llvm/test/MC/AArch64/tme-error.s @@ -0,0 +1,47 @@ +// Tests for transactional memory extension instructions +// RUN: not llvm-mc -triple aarch64 -show-encoding -mattr=+tme < %s 2>&1 | FileCheck %s + +tstart +// CHECK: error: too few operands for instruction +// CHECK-NEXT: tstart +tstart x4, x5 +// CHECK: error: invalid operand for instruction +// CHECK-NEXT: tstart x4, x5 +tstart x4, #1 +// CHECK: error: invalid operand for instruction +// CHECK-NEXT: tstart x4, #1 +tstart sp +// CHECK: error: invalid operand for instruction +// CHECK-NEXT: tstart sp + +ttest +// CHECK: error: too few operands for instruction +// CHECK-NEXT: ttest +ttest x4, x5 +// CHECK: error: invalid operand for instruction +// CHECK-NEXT: ttest x4, x5 +ttest x4, #1 +// CHECK: error: invalid operand for instruction +// CHECK-NEXT: ttest x4, #1 +ttest sp +// CHECK: error: invalid operand for instruction +// CHECK-NEXT: ttest sp + +tcommit x4 +// CHECK: error: invalid operand for instruction +// CHECK-NEXT: tcommit x4 +tcommit sp +// CHECK: error: invalid operand for instruction +// CHECK-NEXT: tcommit sp + + +tcancel +// CHECK: error: too few operands for instruction +// CHECK-NEXT tcancel +tcancel x0 +// CHECK: error: immediate must be an integer in range [0, 65535] +// CHECK-NEXT tcancel +tcancel #65536 +// CHECK: error: immediate must be an integer in range [0, 65535] +// CHECK-NEXT: tcancel #65536 + diff --git a/llvm/test/MC/AArch64/tme.s b/llvm/test/MC/AArch64/tme.s new file mode 100644 --- /dev/null +++ b/llvm/test/MC/AArch64/tme.s @@ -0,0 +1,24 @@ +// Tests for transaction memory extension instructions +// +// RUN: llvm-mc -triple aarch64 -show-encoding -mattr=+tme < %s | FileCheck %s +// RUN: not llvm-mc -triple aarch64 -show-encoding -mattr=-tme < %s 2>&1 | FileCheck %s --check-prefix=NOTME + +tstart x3 +ttest x4 +tcommit +tcancel #0x1234 + +// CHECK: tstart x3 // encoding: [0x63,0x30,0x23,0xd5] +// CHECK: ttest x4 // encoding: [0x64,0x31,0x23,0xd5] +// CHECK: tcommit // encoding: [0x7f,0x30,0x03,0xd5] +// CHECK: tcancel #0x1234 // encoding: [0x80,0x46,0x62,0xd4] + + +// NOTME: instruction requires: tme +// NOTME-NEXT: tstart x3 +// NOTME: instruction requires: tme +// NOTME-NEXT: ttest x4 +// NOTME: instruction requires: tme +// NOTME-NEXT: tcommit +// NOTME: instruction requires: tme +// NOTME-NEXT: tcancel #0x1234 diff --git a/llvm/test/MC/Disassembler/AArch64/tme.txt b/llvm/test/MC/Disassembler/AArch64/tme.txt new file mode 100644 --- /dev/null +++ b/llvm/test/MC/Disassembler/AArch64/tme.txt @@ -0,0 +1,19 @@ +# Tests for transaction memory extension instructions +# RUN: llvm-mc -triple=aarch64 -mattr=+tme -disassemble < %s | FileCheck %s +# RUN: not llvm-mc -triple=aarch64 -mattr=-tme -disassemble < %s 2>&1 | FileCheck %s --check-prefix=NOTME + +[0x63,0x30,0x23,0xd5] +[0x64,0x31,0x23,0xd5] +[0x7f,0x30,0x03,0xd5] +[0x80,0x46,0x62,0xd4] + +# CHECK: tstart x3 +# CHECK: ttest x4 +# CHECK: tcommit +# CHECK: tcancel #0x1234 + +# NOTEME: mrs +# NOTEME-NEXT: mrs + NOTEME-NEXT: msr +# NOTME: warning: invalid instruction encoding +# NOTME-NEXT: [0x80,0x46,0x62,0xd4] diff --git a/llvm/unittests/Support/TargetParserTest.cpp b/llvm/unittests/Support/TargetParserTest.cpp --- a/llvm/unittests/Support/TargetParserTest.cpp +++ b/llvm/unittests/Support/TargetParserTest.cpp @@ -1118,6 +1118,7 @@ {"rcpc", "norcpc", "+rcpc", "-rcpc" }, {"rng", "norng", "+rand", "-rand"}, {"memtag", "nomemtag", "+mte", "-mte"}, + {"tme", "notme", "+tme", "-tme"}, {"ssbs", "nossbs", "+ssbs", "-ssbs"}, {"sb", "nosb", "+sb", "-sb"}, {"predres", "nopredres", "+predres", "-predres"} diff --git a/llvm/utils/TableGen/CodeGenDAGPatterns.cpp b/llvm/utils/TableGen/CodeGenDAGPatterns.cpp --- a/llvm/utils/TableGen/CodeGenDAGPatterns.cpp +++ b/llvm/utils/TableGen/CodeGenDAGPatterns.cpp @@ -2779,7 +2779,7 @@ // chain. if (Int.IS.RetVTs.empty()) Operator = getDAGPatterns().get_intrinsic_void_sdnode(); - else if (Int.ModRef != CodeGenIntrinsic::NoMem) + else if (Int.ModRef != CodeGenIntrinsic::NoMem || Int.hasSideEffects) // Has side-effects, requires chain. Operator = getDAGPatterns().get_intrinsic_w_chain_sdnode(); else // Otherwise, no chain. diff --git a/llvm/utils/TableGen/IntrinsicEmitter.cpp b/llvm/utils/TableGen/IntrinsicEmitter.cpp --- a/llvm/utils/TableGen/IntrinsicEmitter.cpp +++ b/llvm/utils/TableGen/IntrinsicEmitter.cpp @@ -685,7 +685,7 @@ } if (!intrinsic.canThrow || - intrinsic.ModRef != CodeGenIntrinsic::ReadWriteMem || + (intrinsic.ModRef != CodeGenIntrinsic::ReadWriteMem && !intrinsic.hasSideEffects) || intrinsic.isNoReturn || intrinsic.isCold || intrinsic.isNoDuplicate || intrinsic.isConvergent || intrinsic.isSpeculatable) { OS << " const Attribute::AttrKind Atts[] = {"; @@ -727,6 +727,8 @@ switch (intrinsic.ModRef) { case CodeGenIntrinsic::NoMem: + if (intrinsic.hasSideEffects) + break; if (addComma) OS << ","; OS << "Attribute::ReadNone";