// TODO: Support more builtins.
// TODO: Added feature constraints.
+TARGET_BUILTIN(__builtin_loongarch_cacop_d, "vLiULiLi", "nc", "64bit")
+TARGET_BUILTIN(__builtin_loongarch_cacop_w, "viUii", "nc", "32bit")
TARGET_BUILTIN(__builtin_loongarch_dbar, "vIUi", "nc", "")
TARGET_BUILTIN(__builtin_loongarch_ibar, "vIUi", "nc", "")
TARGET_BUILTIN(__builtin_loongarch_movfcsr2gr, "UiIUi", "nc", "f")
def warn_unsafe_buffer_variable : Warning<
"variable %0 participates in unchecked buffer operations">,
InGroup<UnsafeBufferUsage>, DefaultIgnore;
+def err_loongarch_builtin_requires_la32 : Error<
+ "this builtin requires target: loongarch32">;
} // end of sema component.
const std::vector<std::string> &FeaturesVec) const {
if (getTriple().getArch() == llvm::Triple::loongarch64)
Features["64bit"] = true;
+ if (getTriple().getArch() == llvm::Triple::loongarch32)
+ Features["32bit"] = true;
return TargetInfo::initFeatureMap(Features, Diags, CPU, FeaturesVec);
}
switch (BuiltinID) {
default:
llvm_unreachable("unexpected builtin ID.");
+ case LoongArch::BI__builtin_loongarch_cacop_d:
+ ID = Intrinsic::loongarch_cacop_d;
+ break;
+ case LoongArch::BI__builtin_loongarch_cacop_w:
+ ID = Intrinsic::loongarch_cacop_w;
+ break;
case LoongArch::BI__builtin_loongarch_dbar:
ID = Intrinsic::loongarch_dbar;
break;
#define __break(/*ui15*/ _1) __builtin_loongarch_break((_1))
+#if __loongarch_grlen == 32
+#define __cacop_w(/*uimm5*/ _1, /*unsigned int*/ _2, /*simm12*/ _3) \
+ ((void)__builtin_loongarch_cacop_w((_1), (unsigned int)(_2), (_3)))
+#endif
+
+#if __loongarch_grlen == 64
+#define __cacop_d(/*uimm5*/ _1, /*unsigned long int*/ _2, /*simm12*/ _3) \
+ ((void)__builtin_loongarch_cacop_d((_1), (unsigned long int)(_2), (_3)))
+#endif
+
#define __dbar(/*ui15*/ _1) __builtin_loongarch_dbar((_1))
#define __ibar(/*ui15*/ _1) __builtin_loongarch_ibar((_1))
switch (BuiltinID) {
default:
break;
+ case LoongArch::BI__builtin_loongarch_cacop_d:
+ if (!TI.hasFeature("64bit"))
+ return Diag(TheCall->getBeginLoc(),
+ diag::err_loongarch_builtin_requires_la64)
+ << TheCall->getSourceRange();
+ LLVM_FALLTHROUGH;
+ case LoongArch::BI__builtin_loongarch_cacop_w: {
+ if (BuiltinID == LoongArch::BI__builtin_loongarch_cacop_w &&
+ !TI.hasFeature("32bit"))
+ return Diag(TheCall->getBeginLoc(),
+ diag::err_loongarch_builtin_requires_la32)
+ << TheCall->getSourceRange();
+ SemaBuiltinConstantArgRange(TheCall, 0, 0, llvm::maxUIntN(5));
+ SemaBuiltinConstantArgRange(TheCall, 2, llvm::minIntN(12),
+ llvm::maxIntN(12));
+ break;
+ }
case LoongArch::BI__builtin_loongarch_crc_w_b_w:
case LoongArch::BI__builtin_loongarch_crc_w_h_w:
case LoongArch::BI__builtin_loongarch_crc_w_w_w:
#include <larchintrin.h>
+void cacop_d(unsigned long int a) {
+ __builtin_loongarch_cacop_d(1, a, 1024); // expected-error {{this builtin requires target: loongarch64}}
+ __builtin_loongarch_cacop_w(-1, a, 1024); // expected-error {{argument value -1 is outside the valid range [0, 31]}}
+ __builtin_loongarch_cacop_w(32, a, 1024); // expected-error {{argument value 32 is outside the valid range [0, 31]}}
+ __builtin_loongarch_cacop_w(1, a, -4096); // expected-error {{argument value -4096 is outside the valid range [-2048, 2047]}}
+ __builtin_loongarch_cacop_w(1, a, 4096); // expected-error {{argument value 4096 is outside the valid range [-2048, 2047]}}
+}
+
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]}}
__movgr2fcsr(1, a);
__builtin_loongarch_movgr2fcsr(1, a);
}
+
+// CHECK-LABEL: @cacop_w(
+// CHECK-NEXT: entry:
+// CHECK-NEXT: tail call void @llvm.loongarch.cacop.w(i32 1, i32 [[A:%.*]], i32 1024)
+// CHECK-NEXT: tail call void @llvm.loongarch.cacop.w(i32 1, i32 [[A]], i32 1024)
+// CHECK-NEXT: ret void
+//
+void cacop_w(unsigned long int a) {
+ __cacop_w(1, a, 1024);
+ __builtin_loongarch_cacop_w(1, a, 1024);
+}
return 0;
}
+// CHECK-LABEL: @cacop_d(
+// CHECK-NEXT: entry:
+// CHECK-NEXT: tail call void @llvm.loongarch.cacop.d(i64 1, i64 [[A:%.*]], i64 1024)
+// CHECK-NEXT: tail call void @llvm.loongarch.cacop.d(i64 1, i64 [[A]], i64 1024)
+// CHECK-NEXT: ret void
+//
+void cacop_d(unsigned long int a) {
+ __cacop_d(1, a, 1024);
+ __builtin_loongarch_cacop_d(1, a, 1024);
+}
+
// CHECK-LABEL: @crc_w_d_w(
// CHECK-NEXT: entry:
// CHECK-NEXT: [[TMP0:%.*]] = tail call i32 @llvm.loongarch.crc.w.d.w(i64 [[A:%.*]], i32 [[B:%.*]])
// RUN: %clang --target=loongarch32 -S -emit-llvm %s -o - | FileCheck %s --check-prefix=LA32
// RUN: %clang --target=loongarch64 -S -emit-llvm %s -o - | FileCheck %s --check-prefix=LA64
-// LA32-NOT: "target-features"=
+// LA32: "target-features"="+32bit"
// LA64: "target-features"="+64bit,+d,+f"
int foo(void) {
// LoongArch BASE
def int_loongarch_break : Intrinsic<[], [llvm_i32_ty], [ImmArg<ArgIndex<0>>]>;
+def int_loongarch_cacop_d : Intrinsic<[], [llvm_i64_ty, llvm_i64_ty, llvm_i64_ty],
+ [ImmArg<ArgIndex<0>>, ImmArg<ArgIndex<2>>]>;
+def int_loongarch_cacop_w : Intrinsic<[], [llvm_i32_ty, llvm_i32_ty, llvm_i32_ty],
+ [ImmArg<ArgIndex<0>>, ImmArg<ArgIndex<2>>]>;
def int_loongarch_dbar : Intrinsic<[], [llvm_i32_ty], [ImmArg<ArgIndex<0>>]>;
def int_loongarch_ibar : Intrinsic<[], [llvm_i32_ty], [ImmArg<ArgIndex<0>>]>;
def int_loongarch_movfcsr2gr : Intrinsic<[llvm_i32_ty], [llvm_i32_ty],
SDLoc DL(Op);
MVT GRLenVT = Subtarget.getGRLenVT();
SDValue Op0 = Op.getOperand(0);
+ uint64_t IntrinsicEnum = Op.getConstantOperandVal(1);
SDValue Op2 = Op.getOperand(2);
const StringRef ErrorMsgOOR = "out of range";
- switch (Op.getConstantOperandVal(1)) {
+ switch (IntrinsicEnum) {
default:
// TODO: Add more Intrinsics.
return SDValue();
+ case Intrinsic::loongarch_cacop_d:
+ case Intrinsic::loongarch_cacop_w: {
+ if (IntrinsicEnum == Intrinsic::loongarch_cacop_d && !Subtarget.is64Bit()) {
+ DAG.getContext()->emitError(
+ "llvm.loongarch.cacop.d requires target: loongarch64");
+ return Op.getOperand(0);
+ }
+ if (IntrinsicEnum == Intrinsic::loongarch_cacop_w && Subtarget.is64Bit()) {
+ DAG.getContext()->emitError(
+ "llvm.loongarch.cacop.w requires target: loongarch32");
+ return Op.getOperand(0);
+ }
+ // call void @llvm.loongarch.cacop.[d/w](uimm5, rj, simm12)
+ unsigned Imm1 = cast<ConstantSDNode>(Op2)->getZExtValue();
+ if (!isUInt<5>(Imm1))
+ return emitIntrinsicErrorMessage(Op, ErrorMsgOOR, DAG);
+ SDValue Op4 = Op.getOperand(4);
+ int Imm2 = cast<ConstantSDNode>(Op4)->getSExtValue();
+ if (!isInt<12>(Imm2))
+ return emitIntrinsicErrorMessage(Op, ErrorMsgOOR, DAG);
+
+ return Op;
+ }
+
case Intrinsic::loongarch_dbar: {
unsigned Imm = cast<ConstantSDNode>(Op2)->getZExtValue();
if (!isUInt<15>(Imm))
NODE_NAME_CASE(CPUCFG)
NODE_NAME_CASE(MOVGR2FCSR)
NODE_NAME_CASE(MOVFCSR2GR)
+ NODE_NAME_CASE(CACOP_D)
+ NODE_NAME_CASE(CACOP_W)
}
#undef NODE_NAME_CASE
return nullptr;
BITREV_4B,
BITREV_W,
- // Intrinsic operations
+ // Intrinsic operations start ============================================
BREAK,
+ CACOP_D,
+ CACOP_W,
DBAR,
IBAR,
SYSCALL,
// Read CPU configuration information operation
CPUCFG,
+ // Intrinsic operations end =============================================
};
} // end namespace LoongArchISD
def RDTIMEH_W : RDTIME_2R<0b0000000000000000011001, "rdtimeh.w">;
def CPUCFG : ALU_2R<0b0000000000000000011011, "cpucfg">;
+// Cache Maintenance Instructions
+def CACOP : FmtCACOP<(outs), (ins uimm5:$op, GPR:$rj, simm12:$imm12), "cacop",
+ "$op, $rj, $imm12">;
+
/// LA64 instructions
let Predicates = [IsLA64] in {
/// Intrinsics
+def : Pat<(int_loongarch_cacop_d timm:$op, i64:$rj, timm:$imm12),
+ (CACOP uimm5:$op, GPR:$rj, simm12:$imm12)>;
+def : Pat<(int_loongarch_cacop_w i32:$op, i32:$rj, i32:$imm12),
+ (CACOP uimm5:$op, GPR:$rj, simm12:$imm12)>;
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 IOCSRWR_D : IOCSRWR<0b0000011001001000000111, "iocsrwr.d">;
} // Predicates = [IsLA64]
-// Cache Maintenance Instructions
-def CACOP : FmtCACOP<(outs), (ins uimm5:$op, GPR:$rj, simm12:$imm12), "cacop",
- "$op, $rj, $imm12">;
-
// TLB Maintenance Instructions
def TLBSRCH : FmtI32<0b00000110010010000010100000000000, "tlbsrch">;
def TLBRD : FmtI32<0b00000110010010000010110000000000, "tlbrd">;
; RUN: not llc --mtriple=loongarch32 --disable-verify < %s 2>&1 | FileCheck %s
+declare void @llvm.loongarch.cacop.w(i32, i32, i32)
declare i32 @llvm.loongarch.crc.w.b.w(i32, i32)
declare i32 @llvm.loongarch.crc.w.h.w(i32, i32)
declare i32 @llvm.loongarch.crc.w.w.w(i32, i32)
declare i64 @llvm.loongarch.lddir.d(i64, i32)
declare void @llvm.loongarch.ldpte.d(i64, i32)
+define void @cacop_arg0_out_of_hi_range(i32 %a) nounwind {
+; CHECK: argument to 'llvm.loongarch.cacop.w' out of range
+entry:
+ call void @llvm.loongarch.cacop.w(i32 32, i32 %a, i32 1024)
+ ret void
+}
+
+define void @cacop_arg0_out_of_lo_range(i32 %a) nounwind {
+; CHECK: argument to 'llvm.loongarch.cacop.w' out of range
+entry:
+ call void @llvm.loongarch.cacop.w(i32 -1, i32 %a, i32 1024)
+ ret void
+}
+
+define void @cacop_arg2_out_of_hi_range(i32 %a) nounwind {
+; CHECK: argument to 'llvm.loongarch.cacop.w' out of range
+entry:
+ call void @llvm.loongarch.cacop.w(i32 1, i32 %a, i32 4096)
+ ret void
+}
+
+define void @cacop_arg2_out_of_lo_range(i32 %a) nounwind {
+; CHECK: argument to 'llvm.loongarch.cacop.w' out of range
+entry:
+ call void @llvm.loongarch.cacop.w(i32 1, i32 %a, i32 -4096)
+ ret void
+}
+
define i32 @crc_w_b_w(i32 %a, i32 %b) nounwind {
; CHECK: llvm.loongarch.crc.w.b.w requires target: loongarch64
entry:
--- /dev/null
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
+; RUN: llc --mtriple=loongarch32 < %s | FileCheck %s
+
+declare void @llvm.loongarch.cacop.w(i32, i32, i32)
+
+define void @cacop_w(i32 %a) nounwind {
+; CHECK-LABEL: cacop_w:
+; CHECK: # %bb.0:
+; CHECK-NEXT: cacop 1, $a0, 4
+; CHECK-NEXT: ret
+ call void @llvm.loongarch.cacop.w(i32 1, i32 %a, i32 4)
+ ret void
+}
; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
; RUN: not llc --mtriple=loongarch64 < %s 2>&1 | FileCheck %s
+declare void @llvm.loongarch.cacop.w(i32, i32, i32)
+declare void @llvm.loongarch.cacop.d(i64, i64, i64)
declare i64 @llvm.loongarch.csrrd.d(i32 immarg)
declare i64 @llvm.loongarch.csrwr.d(i64, i32 immarg)
declare i64 @llvm.loongarch.csrxchg.d(i64, i64, i32 immarg)
%0 = call i64 @llvm.loongarch.csrxchg.d(i64 %a, i64 %b, i32 -1)
ret i64 %0
}
+
+define void @cacop_w(i32 %a) nounwind {
+; CHECK: llvm.loongarch.cacop.w requires target: loongarch32
+ call void @llvm.loongarch.cacop.w(i32 1, i32 %a, i32 4)
+ ret void
+}
+
+define void @cacop_arg0_out_of_hi_range(i64 %a) nounwind {
+; CHECK: argument to 'llvm.loongarch.cacop.d' out of range
+entry:
+ call void @llvm.loongarch.cacop.d(i64 32, i64 %a, i64 1024)
+ ret void
+}
+
+define void @cacop_arg0_out_of_lo_range(i64 %a) nounwind {
+; CHECK: argument to 'llvm.loongarch.cacop.d' out of range
+entry:
+ call void @llvm.loongarch.cacop.d(i64 -1, i64 %a, i64 1024)
+ ret void
+}
+
+define void @cacop_arg2_out_of_hi_range(i64 %a) nounwind {
+; CHECK: argument to 'llvm.loongarch.cacop.d' out of range
+entry:
+ call void @llvm.loongarch.cacop.d(i64 1, i64 %a, i64 4096)
+ ret void
+}
+
+define void @cacop_arg2_out_of_lo_range(i64 %a) nounwind {
+; CHECK: argument to 'llvm.loongarch.cacop.d' out of range
+entry:
+ call void @llvm.loongarch.cacop.d(i64 1, i64 %a, i64 -4096)
+ ret void
+}
; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
; RUN: llc --mtriple=loongarch64 < %s | FileCheck %s
+declare void @llvm.loongarch.cacop.d(i64, i64, i64)
declare i32 @llvm.loongarch.crc.w.b.w(i32, i32)
declare i32 @llvm.loongarch.crc.w.h.w(i32, i32)
declare i32 @llvm.loongarch.crc.w.w.w(i32, i32)
ret i32 %res
}
+define void @cacop_d(i64 %a) nounwind {
+; CHECK-LABEL: cacop_d:
+; CHECK: # %bb.0:
+; CHECK-NEXT: cacop 1, $a0, 4
+; CHECK-NEXT: ret
+ call void @llvm.loongarch.cacop.d(i64 1, i64 %a, i64 4)
+ ret void
+}
+
define i32 @crc_w_d_w(i64 %a, i32 %b) nounwind {
; CHECK-LABEL: crc_w_d_w:
; CHECK: # %bb.0: