Index: include/llvm/IR/IntrinsicsARM.td =================================================================== --- include/llvm/IR/IntrinsicsARM.td +++ include/llvm/IR/IntrinsicsARM.td @@ -22,6 +22,13 @@ // and return value are essentially chains, used to force ordering during ISel. def int_arm_space : Intrinsic<[llvm_i32_ty], [llvm_i32_ty, llvm_i32_ty], []>; +// Intrinsics for the compiler to use to assert that the operand is either a +// i8 or i16 that is already zero extended. +def int_arm_codegen_zeroext_i8 : + Intrinsic<[llvm_i32_ty], [llvm_i32_ty], [IntrNoMem]>; +def int_arm_codegen_zeroext_i16 : + Intrinsic<[llvm_i32_ty], [llvm_i32_ty], [IntrNoMem]>; + // 16-bit multiplications def int_arm_smulbb : GCCBuiltin<"__builtin_arm_smulbb">, Intrinsic<[llvm_i32_ty], [llvm_i32_ty, llvm_i32_ty], [IntrNoMem]>; Index: lib/Target/ARM/ARMISelLowering.cpp =================================================================== --- lib/Target/ARM/ARMISelLowering.cpp +++ lib/Target/ARM/ARMISelLowering.cpp @@ -3376,6 +3376,12 @@ } return Result; } + case Intrinsic::arm_codegen_zeroext_i8: + return DAG.getNode(ISD::AssertZext, dl, MVT::i32, Op->getOperand(1), + DAG.getValueType(MVT::i8)); + case Intrinsic::arm_codegen_zeroext_i16: + return DAG.getNode(ISD::AssertZext, dl, MVT::i32, Op->getOperand(1), + DAG.getValueType(MVT::i16)); case Intrinsic::arm_neon_vabs: return DAG.getNode(ISD::ABS, SDLoc(Op), Op.getValueType(), Op.getOperand(1)); Index: test/CodeGen/ARM/codegen-zeroext-intrinsic.ll =================================================================== --- /dev/null +++ test/CodeGen/ARM/codegen-zeroext-intrinsic.ll @@ -0,0 +1,45 @@ +; RUN: llc -mtriple=armv8 %s -o - | FileCheck %s +; RUN: llc -mtriple=thumbv8m.main %s -o - | FileCheck %s + +; CHECK-LABEL: zero_i8 +; CHECK-NOT: uxt +; CHECK: bx +define zeroext i8 @zero_i8(i32 %arg) { +entry: + %zeroext = call i32 @llvm.arm.codegen.zeroext.i8(i32 %arg) + %trunc = trunc i32 %zeroext to i8 + ret i8 %trunc +} + +; CHECK-LABEL: zero_i8_mismatch +; CHECK-NOT: uxt +; CHECK: bx +define zeroext i16 @zero_i8_mismatch(i32 %arg) { +entry: + %zeroext = call i32 @llvm.arm.codegen.zeroext.i8(i32 %arg) + %trunc = trunc i32 %zeroext to i16 + ret i16 %trunc +} + +; CHECK-LABEL: zero_i16 +; CHECK-NOT: uxt +; CHECK: bx +define zeroext i16 @zero_i16(i32 %arg) { +entry: + %zeroext = call i32 @llvm.arm.codegen.zeroext.i16(i32 %arg) + %trunc = trunc i32 %zeroext to i16 + ret i16 %trunc +} + +; CHECK-LABEL: zero_i16_mismatch +; CHECK: uxtb r0, r0 +; CHECK: bx +define zeroext i8 @zero_i16_mismatch(i32 %arg) { +entry: + %zeroext = call i32 @llvm.arm.codegen.zeroext.i16(i32 %arg) + %trunc = trunc i32 %zeroext to i8 + ret i8 %trunc +} + +declare i32 @llvm.arm.codegen.zeroext.i8(i32) +declare i32 @llvm.arm.codegen.zeroext.i16(i32)