Author: Bill Wendling Date: 2026-11-02T15:52:39-08:00 New Revision: c62494af27791b6545e0389981b7e082e2cc661b
URL: https://github.com/llvm/llvm-project/commit/c62494af27791b6545e0389981b7e082e2cc661b DIFF: https://github.com/llvm/llvm-project/commit/c62494af27791b6545e0389981b7e082e2cc661b.diff LOG: [Clang][CodeGen] Don't crash on a struct inline asm output in a mask register (#236728) A struct register output with "=k" crashes when x86's 'adjustInlineAsmType' returned '<N x i1>' for "k". The conversion chain then bitcast that vector to the struct type, which is invalid IR. Fix this by casting the vector to an integer that's the same size as the vector, then truncate it to the destination's size. Fixes: 8d6e82d501cd ("[X86] Use vXi1 for `k` constraint in inline asm (#77733)") Assisted-by: Claude Opus 5.5 Added: clang/test/CodeGen/X86/inline-asm-mask-output.c Modified: clang/lib/CodeGen/CGStmt.cpp Removed: ################################################################################ diff --git a/clang/lib/CodeGen/CGStmt.cpp b/clang/lib/CodeGen/CGStmt.cpp index f78a6f54820e93..3f02803fa0264b 100644 --- a/clang/lib/CodeGen/CGStmt.cpp +++ b/clang/lib/CodeGen/CGStmt.cpp @@ -3348,9 +3348,15 @@ void AsmConstraintsInfo::EmitAsmStores( Builder.CreateCall(FnAssume, IsBooleanValue); } + // ResultTypeRequiresCast elements correspond to the first + // ResultTypeRequiresCast.size() elements of RegResults. + const bool RequiresCast = + i < ResultTypeRequiresCast.size() && ResultTypeRequiresCast[i]; + // If the result type of the LLVM IR asm doesn't match the result type of - // the expression, do the conversion. - if (ResultRegTypes[i] != TruncTy) { + // the expression, do the conversion. An output returned as an integer is + // converted where it's stored below. + if (ResultRegTypes[i] != TruncTy && !RequiresCast) { // Truncate the integer result to the right size, note that TruncTy can be // a pointer. if (TruncTy->isFloatingPointTy()) @@ -3376,9 +3382,7 @@ void AsmConstraintsInfo::EmitAsmStores( ApplyAtomGroup Grp(CGF.getDebugInfo()); LValue Dest = ResultRegDests[i]; - // ResultTypeRequiresCast elements correspond to the first - // ResultTypeRequiresCast.size() elements of RegResults. - if (i < ResultTypeRequiresCast.size() && ResultTypeRequiresCast[i]) { + if (RequiresCast) { unsigned Size = getContext().getTypeSize(ResultRegQualTys[i]); Address A = Dest.getAddress().withElementType(ResultRegTypes[i]); @@ -3397,9 +3401,10 @@ void AsmConstraintsInfo::EmitAsmStores( return; } - // An output tied to a larger input has the input's type, which may not be - // an integer (e.g., a pointer). Convert it to an integer of the same - // size, then store only the destination's bits. + // The output's type may not be an integer: an output tied to a larger + // input has the input's type (e.g., a pointer), and a mask register + // output ("k" on X86) is a vector of i1. Convert it to an integer of the + // same size, then store only the destination's bits. llvm::Type *IntTy = llvm::IntegerType::get(CTX, Size); uint64_t TmpSize = CGM.getDataLayout().getTypeSizeInBits(Tmp->getType()); Tmp = Builder.CreateBitOrPointerCast( diff --git a/clang/test/CodeGen/X86/inline-asm-mask-output.c b/clang/test/CodeGen/X86/inline-asm-mask-output.c new file mode 100644 index 00000000000000..925d31c799a3e4 --- /dev/null +++ b/clang/test/CodeGen/X86/inline-asm-mask-output.c @@ -0,0 +1,63 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --check-globals none --version 6 +// REQUIRES: x86-registered-target +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -target-feature +avx512f -emit-llvm %s -o - | FileCheck %s + +// A struct output in a mask register ("k") is returned as a vector of i1. It's +// stored to the destination as an integer of the destination's size. + +struct C { + char a, b; +}; + +// CHECK-LABEL: define dso_local void @test_mask_output( +// CHECK-SAME: ptr noundef [[P:%.*]]) #[[ATTR0:[0-9]+]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[P_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: store ptr [[P]], ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = call <16 x i1> asm "", "=k,~{dirflag},~{fpsr},~{flags}"() #[[ATTR2:[0-9]+]], !srcloc [[META1:![0-9]+]] +// CHECK-NEXT: [[TMP2:%.*]] = bitcast <16 x i1> [[TMP1]] to i16 +// CHECK-NEXT: store i16 [[TMP2]], ptr [[TMP0]], align 1 +// CHECK-NEXT: ret void +// +void test_mask_output(struct C *p) { + asm("" : "=k"(*p)); +} + +// CHECK-LABEL: define dso_local void @test_mask_inout( +// CHECK-SAME: ptr noundef [[P:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[P_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: store ptr [[P]], ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load i16, ptr [[TMP0]], align 1 +// CHECK-NEXT: [[TMP2:%.*]] = bitcast i16 [[TMP1]] to <16 x i1> +// CHECK-NEXT: [[TMP3:%.*]] = call <16 x i1> asm "", "=k,0,~{dirflag},~{fpsr},~{flags}"(<16 x i1> [[TMP2]]) #[[ATTR2]], !srcloc [[META2:![0-9]+]] +// CHECK-NEXT: [[TMP4:%.*]] = bitcast <16 x i1> [[TMP3]] to i16 +// CHECK-NEXT: store i16 [[TMP4]], ptr [[TMP0]], align 1 +// CHECK-NEXT: ret void +// +void test_mask_inout(struct C *p) { + asm("" : "+k"(*p)); +} + +// An output tied to a larger input is returned as a vector of the input's size. +// CHECK-LABEL: define dso_local void @test_mask_tied_int( +// CHECK-SAME: ptr noundef [[P:%.*]], i32 noundef [[X:%.*]]) #[[ATTR1:[0-9]+]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[P_ADDR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[X_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: store ptr [[P]], ptr [[P_ADDR]], align 8 +// CHECK-NEXT: store i32 [[X]], ptr [[X_ADDR]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[P_ADDR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[X_ADDR]], align 4 +// CHECK-NEXT: [[TMP2:%.*]] = bitcast i32 [[TMP1]] to <32 x i1> +// CHECK-NEXT: [[TMP3:%.*]] = call <32 x i1> asm "", "=k,0,~{dirflag},~{fpsr},~{flags}"(<32 x i1> [[TMP2]]) #[[ATTR2]], !srcloc [[META3:![0-9]+]] +// CHECK-NEXT: [[TMP4:%.*]] = bitcast <32 x i1> [[TMP3]] to i32 +// CHECK-NEXT: [[TMP5:%.*]] = trunc i32 [[TMP4]] to i16 +// CHECK-NEXT: store i16 [[TMP5]], ptr [[TMP0]], align 1 +// CHECK-NEXT: ret void +// +void test_mask_tied_int(struct C *p, int x) { + asm("" : "=k"(*p) : "0"(x)); +} _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
