diff --git a/clang/include/clang/Basic/BuiltinsLoongArch.def b/clang/include/clang/Basic/BuiltinsLoongArch.def --- a/clang/include/clang/Basic/BuiltinsLoongArch.def +++ b/clang/include/clang/Basic/BuiltinsLoongArch.def @@ -18,6 +18,9 @@ // TODO: Support more builtins. // TODO: Added feature constraints. TARGET_BUILTIN(__builtin_loongarch_dbar, "vIUi", "nc", "") +TARGET_BUILTIN(__builtin_loongarch_ibar, "vIUi", "nc", "") +TARGET_BUILTIN(__builtin_loongarch_break, "vIUi", "nc", "") +TARGET_BUILTIN(__builtin_loongarch_syscall, "vIUi", "nc", "") TARGET_BUILTIN(__builtin_loongarch_crc_w_d_w, "iLii", "nc", "64bit") 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 @@ -19663,6 +19663,15 @@ case LoongArch::BI__builtin_loongarch_crc_w_d_w: ID = Intrinsic::loongarch_crc_w_d_w; break; + case LoongArch::BI__builtin_loongarch_break: + ID = Intrinsic::loongarch_break; + break; + case LoongArch::BI__builtin_loongarch_ibar: + ID = Intrinsic::loongarch_ibar; + break; + case LoongArch::BI__builtin_loongarch_syscall: + ID = Intrinsic::loongarch_syscall; + break; // TODO: Support more Intrinsics. } diff --git a/clang/lib/Headers/larchintrin.h b/clang/lib/Headers/larchintrin.h --- a/clang/lib/Headers/larchintrin.h +++ b/clang/lib/Headers/larchintrin.h @@ -22,8 +22,14 @@ } #endif +#define __break(/*ui15*/ _1) __builtin_loongarch_break((_1)) + #define __dbar(/*ui15*/ _1) __builtin_loongarch_dbar((_1)) +#define __ibar(/*ui15*/ _1) __builtin_loongarch_ibar((_1)) + +#define __syscall(/*ui15*/ _1) __builtin_loongarch_syscall((_1)) + #ifdef __cplusplus } #endif diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp --- a/clang/lib/Sema/SemaChecking.cpp +++ b/clang/lib/Sema/SemaChecking.cpp @@ -3710,7 +3710,10 @@ diag::err_loongarch_builtin_requires_la64) << TheCall->getSourceRange(); break; + case LoongArch::BI__builtin_loongarch_break: case LoongArch::BI__builtin_loongarch_dbar: + case LoongArch::BI__builtin_loongarch_ibar: + case LoongArch::BI__builtin_loongarch_syscall: // Check if immediate is in [0, 32767]. return SemaBuiltinConstantArgRange(TheCall, 0, 0, 32767); } diff --git a/clang/test/CodeGen/LoongArch/intrinsic-error.c b/clang/test/CodeGen/LoongArch/intrinsic-error.c --- a/clang/test/CodeGen/LoongArch/intrinsic-error.c +++ b/clang/test/CodeGen/LoongArch/intrinsic-error.c @@ -7,10 +7,26 @@ return __builtin_loongarch_crc_w_d_w(a, b); // expected-error {{this builtin requires target: loongarch64}} } -void dbar_out_of_hi_range() { - return __builtin_loongarch_dbar(32768); // expected-error {{argument value 32768 is outside the valid range [0, 32767]}} +void dbar(int a) { + __builtin_loongarch_dbar(32768); // expected-error {{argument value 32768 is outside the valid range [0, 32767]}} + __builtin_loongarch_dbar(-1); // expected-error {{argument value 4294967295 is outside the valid range [0, 32767]}} + __builtin_loongarch_dbar(a); // expected-error {{argument to '__builtin_loongarch_dbar' must be a constant integer}} } -void dbar_out_of_lo_range() { - return __builtin_loongarch_dbar(-1); // expected-error {{argument value 4294967295 is outside the valid range [0, 32767]}} +void ibar(int a) { + __builtin_loongarch_ibar(32769); // expected-error {{argument value 32769 is outside the valid range [0, 32767]}} + __builtin_loongarch_ibar(-1); // expected-error {{argument value 4294967295 is outside the valid range [0, 32767]}} + __builtin_loongarch_ibar(a); // expected-error {{argument to '__builtin_loongarch_ibar' must be a constant integer}} +} + +void loongarch_break(int a) { + __builtin_loongarch_break(32769); // expected-error {{argument value 32769 is outside the valid range [0, 32767]}} + __builtin_loongarch_break(-1); // expected-error {{argument value 4294967295 is outside the valid range [0, 32767]}} + __builtin_loongarch_break(a); // expected-error {{argument to '__builtin_loongarch_break' must be a constant integer}} +} + +void syscall(int a) { + __builtin_loongarch_syscall(32769); // expected-error {{argument value 32769 is outside the valid range [0, 32767]}} + __builtin_loongarch_syscall(-1); // expected-error {{argument value 4294967295 is outside the valid range [0, 32767]}} + __builtin_loongarch_syscall(a); // expected-error {{argument to '__builtin_loongarch_syscall' must be a constant integer}} } diff --git a/clang/test/CodeGen/LoongArch/intrinsic.c b/clang/test/CodeGen/LoongArch/intrinsic.c --- a/clang/test/CodeGen/LoongArch/intrinsic.c +++ b/clang/test/CodeGen/LoongArch/intrinsic.c @@ -20,3 +20,44 @@ return __builtin_loongarch_dbar(0); } +// LA32-LABEL: @ibar( +// LA32-NEXT: entry: +// LA32-NEXT: call void @llvm.loongarch.ibar(i32 0) +// LA32-NEXT: ret void +// +// LA64-LABEL: @ibar( +// LA64-NEXT: entry: +// LA64-NEXT: call void @llvm.loongarch.ibar(i32 0) +// LA64-NEXT: ret void +// +void ibar() { + return __builtin_loongarch_ibar(0); +} + +// LA32-LABEL: @loongarch_break( +// LA32-NEXT: entry: +// LA32-NEXT: call void @llvm.loongarch.break(i32 1) +// LA32-NEXT: ret void +// +// LA64-LABEL: @loongarch_break( +// LA64-NEXT: entry: +// LA64-NEXT: call void @llvm.loongarch.break(i32 1) +// LA64-NEXT: ret void +// +void loongarch_break() { + __builtin_loongarch_break(1); +} + +// LA32-LABEL: @syscall( +// LA32-NEXT: entry: +// LA32-NEXT: call void @llvm.loongarch.syscall(i32 1) +// LA32-NEXT: ret void +// +// LA64-LABEL: @syscall( +// LA64-NEXT: entry: +// LA64-NEXT: call void @llvm.loongarch.syscall(i32 1) +// LA64-NEXT: ret void +// +void syscall() { + __builtin_loongarch_syscall(1); +} diff --git a/llvm/include/llvm/IR/IntrinsicsLoongArch.td b/llvm/include/llvm/IR/IntrinsicsLoongArch.td --- a/llvm/include/llvm/IR/IntrinsicsLoongArch.td +++ b/llvm/include/llvm/IR/IntrinsicsLoongArch.td @@ -49,7 +49,11 @@ //===----------------------------------------------------------------------===// // LoongArch BASE -def int_loongarch_dbar : Intrinsic<[], [llvm_i32_ty]>; +def int_loongarch_break : Intrinsic<[], [llvm_i32_ty], [ImmArg>]>; +def int_loongarch_dbar : Intrinsic<[], [llvm_i32_ty], [ImmArg>]>; +def int_loongarch_ibar : Intrinsic<[], [llvm_i32_ty], [ImmArg>]>; +def int_loongarch_syscall : Intrinsic<[], [llvm_i32_ty], [ImmArg>]>; + def int_loongarch_crc_w_d_w : Intrinsic<[llvm_i32_ty], [llvm_i64_ty, llvm_i32_ty]>; } // TargetPrefix = "loongarch" diff --git a/llvm/lib/Target/LoongArch/LoongArchISelLowering.h b/llvm/lib/Target/LoongArch/LoongArchISelLowering.h --- a/llvm/lib/Target/LoongArch/LoongArchISelLowering.h +++ b/llvm/lib/Target/LoongArch/LoongArchISelLowering.h @@ -60,7 +60,10 @@ BITREV_W, // Intrinsic operations + BREAK, DBAR, + IBAR, + SYSCALL, // CRC check operations CRC_W_D_W diff --git a/llvm/lib/Target/LoongArch/LoongArchISelLowering.cpp b/llvm/lib/Target/LoongArch/LoongArchISelLowering.cpp --- a/llvm/lib/Target/LoongArch/LoongArchISelLowering.cpp +++ b/llvm/lib/Target/LoongArch/LoongArchISelLowering.cpp @@ -578,35 +578,64 @@ } } +// Helper function that emits error message for intrinsics with void return +// value. +static SDValue emitIntrinsicErrorMessage(SDValue Op, StringRef Name, + StringRef ErrorMsg, + SelectionDAG &DAG) { + + DAG.getContext()->emitError("argument to '" + Name + "' " + ErrorMsg); + return Op.getOperand(0); +} + SDValue LoongArchTargetLowering::lowerINTRINSIC_VOID(SDValue Op, SelectionDAG &DAG) const { SDLoc DL(Op); MVT GRLenVT = Subtarget.getGRLenVT(); + SDValue Op0 = Op.getOperand(0); + SDValue Op2 = Op.getOperand(2); + const StringRef ErrorMsgOOR = "out of range"; switch (Op.getConstantOperandVal(1)) { default: // TODO: Add more Intrinsics. return SDValue(); case Intrinsic::loongarch_dbar: { - SDValue Op0 = Op.getOperand(0); - SDValue Op2 = Op.getOperand(2); - if (!isa(Op2)) { - DAG.getContext()->emitError("argument to '__builtin_loongarch_dbar' must " - "be a constant integer"); - return Op.getOperand(0); - } unsigned Imm = cast(Op2)->getZExtValue(); - if (!isUInt<15>(Imm)) { - DAG.getContext()->emitError( - "argument to '__builtin_loongarch_dbar' out of range"); - return Op0; - } + if (!isUInt<15>(Imm)) + return emitIntrinsicErrorMessage(Op, "__builtin_loongarch_dbar", + ErrorMsgOOR, DAG); - if (GRLenVT == MVT::i32) - return Op; return DAG.getNode(LoongArchISD::DBAR, DL, MVT::Other, Op0, DAG.getConstant(Imm, DL, GRLenVT)); } + case Intrinsic::loongarch_ibar: { + unsigned Imm = cast(Op2)->getZExtValue(); + if (!isUInt<15>(Imm)) + return emitIntrinsicErrorMessage(Op, "__builtin_loongarch_ibar", + ErrorMsgOOR, DAG); + + return DAG.getNode(LoongArchISD::IBAR, DL, MVT::Other, Op0, + DAG.getConstant(Imm, DL, GRLenVT)); + } + case Intrinsic::loongarch_break: { + unsigned Imm = cast(Op2)->getZExtValue(); + if (!isUInt<15>(Imm)) + return emitIntrinsicErrorMessage(Op, "__builtin_loongarch_break", + ErrorMsgOOR, DAG); + + return DAG.getNode(LoongArchISD::BREAK, DL, MVT::Other, Op0, + DAG.getConstant(Imm, DL, GRLenVT)); + } + case Intrinsic::loongarch_syscall: { + unsigned Imm = cast(Op2)->getZExtValue(); + if (!isUInt<15>(Imm)) + return emitIntrinsicErrorMessage(Op, "__builtin_loongarch_syscall", + ErrorMsgOOR, DAG); + + return DAG.getNode(LoongArchISD::SYSCALL, DL, MVT::Other, Op0, + DAG.getConstant(Imm, DL, GRLenVT)); + } } } @@ -1354,6 +1383,9 @@ NODE_NAME_CASE(CLZ_W) NODE_NAME_CASE(CTZ_W) NODE_NAME_CASE(DBAR) + NODE_NAME_CASE(IBAR) + NODE_NAME_CASE(BREAK) + NODE_NAME_CASE(SYSCALL) NODE_NAME_CASE(CRC_W_D_W) } #undef NODE_NAME_CASE diff --git a/llvm/lib/Target/LoongArch/LoongArchInstrInfo.td b/llvm/lib/Target/LoongArch/LoongArchInstrInfo.td --- a/llvm/lib/Target/LoongArch/LoongArchInstrInfo.td +++ b/llvm/lib/Target/LoongArch/LoongArchInstrInfo.td @@ -35,7 +35,8 @@ SDTCisInt<0>, SDTCisSameAs<0, 1>, SDTCisInt<2>, SDTCisSameAs<2, 3> ]>; -def SDT_LoongArchDBAR : SDTypeProfile<0, 1, [SDTCisVT<0, GRLenVT>]>; +// "VI" means no output and an integer input. +def SDT_LoongArchVI : SDTypeProfile<0, 1, [SDTCisVT<0, GRLenVT>]>; // TODO: Add LoongArch specific DAG Nodes // Target-independent nodes, but with target-specific formats. @@ -70,8 +71,14 @@ def loongarch_bitrev_w : SDNode<"LoongArchISD::BITREV_W", SDTUnaryOp>; def loongarch_clzw : SDNode<"LoongArchISD::CLZ_W", SDTIntBitCountUnaryOp>; def loongarch_ctzw : SDNode<"LoongArchISD::CTZ_W", SDTIntBitCountUnaryOp>; -def loongarch_dbar : SDNode<"LoongArchISD::DBAR", SDT_LoongArchDBAR, +def loongarch_dbar : SDNode<"LoongArchISD::DBAR", SDT_LoongArchVI, [SDNPHasChain, SDNPSideEffect]>; +def loongarch_ibar : SDNode<"LoongArchISD::IBAR", SDT_LoongArchVI, + [SDNPHasChain, SDNPSideEffect]>; +def loongarch_break : SDNode<"LoongArchISD::BREAK", SDT_LoongArchVI, + [SDNPHasChain, SDNPSideEffect]>; +def loongarch_syscall : SDNode<"LoongArchISD::SYSCALL", SDT_LoongArchVI, + [SDNPHasChain, SDNPSideEffect]>; //===----------------------------------------------------------------------===// // Operand and SDNode transformation definitions. @@ -1341,13 +1348,12 @@ /// Intrinsics -let Predicates = [IsLA32] in { -def : Pat<(int_loongarch_dbar uimm15:$imm15), (DBAR uimm15:$imm15)>; -} // Predicates = [IsLA32] - -let Predicates = [IsLA64] in { def : Pat<(loongarch_dbar uimm15:$imm15), (DBAR uimm15:$imm15)>; +def : Pat<(loongarch_ibar uimm15:$imm15), (IBAR uimm15:$imm15)>; +def : Pat<(loongarch_break uimm15:$imm15), (BREAK uimm15:$imm15)>; +def : Pat<(loongarch_syscall uimm15:$imm15), (SYSCALL uimm15:$imm15)>; +let Predicates = [IsLA64] in { // CRC Check Instructions def : PatGprGpr; } // Predicates = [IsLA64] diff --git a/llvm/test/CodeGen/LoongArch/intrinsic-error.ll b/llvm/test/CodeGen/LoongArch/intrinsic-error.ll --- a/llvm/test/CodeGen/LoongArch/intrinsic-error.ll +++ b/llvm/test/CodeGen/LoongArch/intrinsic-error.ll @@ -1,12 +1,10 @@ -; RUN: not llc --mtriple=loongarch32 --disable-verify < %s 2>&1 | FileCheck %s -; RUN: not llc --mtriple=loongarch64 --disable-verify < %s 2>&1 | FileCheck %s +; RUN: not llc --mtriple=loongarch32 < %s 2>&1 | FileCheck %s +; RUN: not llc --mtriple=loongarch64 < %s 2>&1 | FileCheck %s -define void @dbar_not_constant(i32 %x) nounwind { -; CHECK: argument to '__builtin_loongarch_dbar' must be a constant integer -entry: - call void @llvm.loongarch.dbar(i32 %x) - ret void -} +declare void @llvm.loongarch.dbar(i32) +declare void @llvm.loongarch.ibar(i32) +declare void @llvm.loongarch.break(i32) +declare void @llvm.loongarch.syscall(i32) define void @dbar_imm_out_of_hi_range() nounwind { ; CHECK: argument to '__builtin_loongarch_dbar' out of range @@ -22,4 +20,44 @@ ret void } -declare void @llvm.loongarch.dbar(i32) +define void @ibar_imm_out_of_hi_range() nounwind { +; CHECK: argument to '__builtin_loongarch_ibar' out of range +entry: + call void @llvm.loongarch.ibar(i32 32769) + ret void +} + +define void @ibar_imm_out_of_lo_range() nounwind { +; CHECK: argument to '__builtin_loongarch_ibar' out of range +entry: + call void @llvm.loongarch.ibar(i32 -1) + ret void +} + +define void @break_imm_out_of_hi_range() nounwind { +; CHECK: argument to '__builtin_loongarch_break' out of range +entry: + call void @llvm.loongarch.break(i32 32769) + ret void +} + +define void @break_imm_out_of_lo_range() nounwind { +; CHECK: argument to '__builtin_loongarch_break' out of range +entry: + call void @llvm.loongarch.break(i32 -1) + ret void +} + +define void @syscall_imm_out_of_hi_range() nounwind { +; CHECK: argument to '__builtin_loongarch_syscall' out of range +entry: + call void @llvm.loongarch.syscall(i32 32769) + ret void +} + +define void @syscall_imm_out_of_lo_range() nounwind { +; CHECK: argument to '__builtin_loongarch_syscall' out of range +entry: + call void @llvm.loongarch.syscall(i32 -1) + ret void +} diff --git a/llvm/test/CodeGen/LoongArch/intrinsic-not-constant-error.ll b/llvm/test/CodeGen/LoongArch/intrinsic-not-constant-error.ll new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/LoongArch/intrinsic-not-constant-error.ll @@ -0,0 +1,35 @@ +; RUN: not llc --mtriple=loongarch32 < %s 2>&1 | FileCheck %s +; RUN: not llc --mtriple=loongarch64 < %s 2>&1 | FileCheck %s + +declare void @llvm.loongarch.dbar(i32) +declare void @llvm.loongarch.ibar(i32) +declare void @llvm.loongarch.break(i32) +declare void @llvm.loongarch.syscall(i32) + +define void @dbar_not_constant(i32 %x) nounwind { +; CHECK: immarg operand has non-immediate parameter +entry: + call void @llvm.loongarch.dbar(i32 %x) + ret void +} + +define void @ibar(i32 %x) nounwind { +; CHECK: immarg operand has non-immediate parameter +entry: + call void @llvm.loongarch.ibar(i32 %x) + ret void +} + +define void @break(i32 %x) nounwind { +; CHECK: immarg operand has non-immediate parameter +entry: + call void @llvm.loongarch.break(i32 %x) + ret void +} + +define void @syscall(i32 %x) nounwind { +; CHECK: immarg operand has non-immediate parameter +entry: + call void @llvm.loongarch.syscall(i32 %x) + ret void +} diff --git a/llvm/test/CodeGen/LoongArch/intrinsic.ll b/llvm/test/CodeGen/LoongArch/intrinsic.ll --- a/llvm/test/CodeGen/LoongArch/intrinsic.ll +++ b/llvm/test/CodeGen/LoongArch/intrinsic.ll @@ -3,6 +3,9 @@ ; RUN: llc --mtriple=loongarch64 < %s | FileCheck %s declare void @llvm.loongarch.dbar(i32) +declare void @llvm.loongarch.ibar(i32) +declare void @llvm.loongarch.break(i32) +declare void @llvm.loongarch.syscall(i32) define void @foo() nounwind { ; CHECK-LABEL: foo: @@ -13,3 +16,33 @@ call void @llvm.loongarch.dbar(i32 0) ret void } + +define void @ibar() nounwind { +; CHECK-LABEL: ibar: +; CHECK: # %bb.0: # %entry +; CHECK-NEXT: ibar 0 +; CHECK-NEXT: ret +entry: + call void @llvm.loongarch.ibar(i32 0) + ret void +} + +define void @break() nounwind { +; CHECK-LABEL: break: +; CHECK: # %bb.0: # %entry +; CHECK-NEXT: break 1 +; CHECK-NEXT: ret +entry: + call void @llvm.loongarch.break(i32 1) + ret void +} + +define void @syscall() nounwind { +; CHECK-LABEL: syscall: +; CHECK: # %bb.0: # %entry +; CHECK-NEXT: syscall 1 +; CHECK-NEXT: ret +entry: + call void @llvm.loongarch.syscall(i32 1) + ret void +}