Skip to content

[GlobalISel] Diagnose inline assembly constraint lowering errors #135782

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
May 7, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
70 changes: 37 additions & 33 deletions llvm/lib/CodeGen/GlobalISel/InlineAsmLowering.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
#include "llvm/CodeGen/MachineOperand.h"
#include "llvm/CodeGen/MachineRegisterInfo.h"
#include "llvm/CodeGen/TargetLowering.h"
#include "llvm/IR/DiagnosticInfo.h"
#include "llvm/IR/Module.h"

#define DEBUG_TYPE "inline-asm-lowering"
Expand Down Expand Up @@ -231,6 +232,15 @@ bool InlineAsmLowering::lowerInlineAsm(
TargetLowering::AsmOperandInfoVector TargetConstraints =
TLI->ParseConstraints(DL, TRI, Call);

const auto ConstraintError = [&](const GISelAsmOperandInfo &Info, Twine Msg) {
LLVMContext &Ctx = MIRBuilder.getContext();
Ctx.diagnose(DiagnosticInfoInlineAsm(
Call, "invalid constraint '" + Info.ConstraintCode + "': " + Msg));
// TODO: Recover if fallback isn't used. Otherwise let the fallback to DAG
// kick in.
return false;
};

ExtraFlags ExtraInfo(Call);
unsigned ArgNo = 0; // ArgNo - The argument of the CallInst.
unsigned ResNo = 0; // ResNo - The result number of the next output.
Expand All @@ -243,8 +253,8 @@ bool InlineAsmLowering::lowerInlineAsm(
OpInfo.CallOperandVal = const_cast<Value *>(Call.getArgOperand(ArgNo));

if (isa<BasicBlock>(OpInfo.CallOperandVal)) {
LLVM_DEBUG(dbgs() << "Basic block input operands not supported yet\n");
return false;
return ConstraintError(OpInfo,
"basic block input operands not supported yet");
}

Type *OpTy = OpInfo.CallOperandVal->getType();
Expand All @@ -258,9 +268,8 @@ bool InlineAsmLowering::lowerInlineAsm(

// FIXME: Support aggregate input operands
if (!OpTy->isSingleValueType()) {
LLVM_DEBUG(
dbgs() << "Aggregate input operands are not supported yet\n");
return false;
return ConstraintError(OpInfo,
"aggregate input operands not supported yet");
}

OpInfo.ConstraintVT =
Expand Down Expand Up @@ -344,9 +353,8 @@ bool InlineAsmLowering::lowerInlineAsm(

// Find a register that we can use.
if (OpInfo.Regs.empty()) {
LLVM_DEBUG(dbgs()
<< "Couldn't allocate output register for constraint\n");
return false;
return ConstraintError(
OpInfo, "could not allocate output register for constraint");
}

// Add information to the INLINEASM instruction to know that this
Expand Down Expand Up @@ -389,13 +397,13 @@ bool InlineAsmLowering::lowerInlineAsm(

const InlineAsm::Flag MatchedOperandFlag(Inst->getOperand(InstFlagIdx).getImm());
if (MatchedOperandFlag.isMemKind()) {
LLVM_DEBUG(dbgs() << "Matching input constraint to mem operand not "
"supported. This should be target specific.\n");
return false;
return ConstraintError(
OpInfo,
"matching input constraint to mem operand not supported; this "
"should be target specific");
}
if (!MatchedOperandFlag.isRegDefKind() && !MatchedOperandFlag.isRegDefEarlyClobberKind()) {
LLVM_DEBUG(dbgs() << "Unknown matching constraint\n");
return false;
return ConstraintError(OpInfo, "unknown matching constraint");
}

// We want to tie input to register in next operand.
Expand Down Expand Up @@ -425,9 +433,10 @@ bool InlineAsmLowering::lowerInlineAsm(

if (OpInfo.ConstraintType == TargetLowering::C_Other &&
OpInfo.isIndirect) {
LLVM_DEBUG(dbgs() << "Indirect input operands with unknown constraint "
"not supported yet\n");
return false;
return ConstraintError(
OpInfo,
"indirect input operands with unknown constraint not supported "
"yet");
}

if (OpInfo.ConstraintType == TargetLowering::C_Immediate ||
Expand All @@ -437,9 +446,7 @@ bool InlineAsmLowering::lowerInlineAsm(
if (!lowerAsmOperandForConstraint(OpInfo.CallOperandVal,
OpInfo.ConstraintCode, Ops,
MIRBuilder)) {
LLVM_DEBUG(dbgs() << "Don't support constraint: "
<< OpInfo.ConstraintCode << " yet\n");
return false;
return ConstraintError(OpInfo, "unsupported constraint");
}

assert(Ops.size() > 0 &&
Expand All @@ -456,9 +463,8 @@ bool InlineAsmLowering::lowerInlineAsm(
if (OpInfo.ConstraintType == TargetLowering::C_Memory) {

if (!OpInfo.isIndirect) {
LLVM_DEBUG(dbgs()
<< "Cannot indirectify memory input operands yet\n");
return false;
return ConstraintError(
OpInfo, "indirect memory input operands are not supported yet");
}

assert(OpInfo.isIndirect && "Operand must be indirect to be a mem!");
Expand All @@ -482,18 +488,15 @@ bool InlineAsmLowering::lowerInlineAsm(
"Unknown constraint type!");

if (OpInfo.isIndirect) {
LLVM_DEBUG(dbgs() << "Can't handle indirect register inputs yet "
"for constraint '"
<< OpInfo.ConstraintCode << "'\n");
return false;
return ConstraintError(
OpInfo, "indirect register inputs are not supported yet");
}

// Copy the input into the appropriate registers.
if (OpInfo.Regs.empty()) {
LLVM_DEBUG(
dbgs()
<< "Couldn't allocate input register for register constraint\n");
return false;
return ConstraintError(
OpInfo,
"could not allocate input register for register constraint");
}

unsigned NumRegs = OpInfo.Regs.size();
Expand All @@ -503,9 +506,10 @@ bool InlineAsmLowering::lowerInlineAsm(
"source registers");

if (NumRegs > 1) {
LLVM_DEBUG(dbgs() << "Input operands with multiple input registers are "
"not supported yet\n");
return false;
return ConstraintError(
OpInfo,
"input operands with multiple input registers are not supported "
"yet");
}

InlineAsm::Flag Flag(InlineAsm::Kind::RegUse, NumRegs);
Expand Down
6 changes: 5 additions & 1 deletion llvm/test/CodeGen/AArch64/GlobalISel/arm64-fallback.ll
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
; RUN: llc -O0 -global-isel -global-isel-abort=2 -pass-remarks-missed='gisel*' -verify-machineinstrs %s -o %t.out 2> %t.err
; RUN: not llc -O0 -global-isel -global-isel-abort=2 -pass-remarks-missed='gisel*' -verify-machineinstrs %s -o - > %t.out 2> %t.err
; RUN: FileCheck %s --check-prefix=FALLBACK-WITH-REPORT-OUT < %t.out
; RUN: FileCheck %s --check-prefix=FALLBACK-WITH-REPORT-ERR < %t.err
; RUN: not --crash llc -global-isel -mtriple aarch64_be %s -o - 2>&1 | FileCheck %s --check-prefix=BIG-ENDIAN

; This file checks that the fallback path to selection dag works.
; The test is fragile in the sense that it must be updated to expose
; something that fails with global-isel.
; When we cannot produce a test case anymore, that means we can remove
; the fallback path.

; -o - > %t.out is used instead of -o %t.out because llc does not write the output
; file if an error is emitted, but it will still print to stdout.

target datalayout = "e-m:o-i64:64-i128:128-n32:64-S128"
target triple = "aarch64--"

Expand Down
2 changes: 1 addition & 1 deletion llvm/test/CodeGen/AArch64/arm64-preserve-all.ll
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ define preserve_allcc void @preserve_all() {
define dso_local void @normal_cc_caller() {
entry:
%v = alloca i32, align 4
call void asm sideeffect "mov x9, $0", "N,~{x9}"(i32 48879) #2
call void asm sideeffect "mov x9, $0", "n,~{x9}"(i32 48879) #2
call void asm sideeffect "movi v9.2d, #0","~{v9}" () #2


Expand Down
2 changes: 1 addition & 1 deletion llvm/test/CodeGen/AArch64/arm64-preserve-most.ll
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ define preserve_mostcc void @preserve_most() {
define dso_local void @normal_cc_caller() {
entry:
%v = alloca i32, align 4
call void asm sideeffect "mov x9, $0", "N,~{x9}"(i32 48879) #2
call void asm sideeffect "mov x9, $0", "n,~{x9}"(i32 48879) #2
call preserve_mostcc void @preserve_most()
%0 = load i32, ptr %v, align 4
%1 = call i32 asm sideeffect "mov ${0:w}, w9", "=r,r"(i32 %0) #2
Expand Down
30 changes: 30 additions & 0 deletions llvm/test/CodeGen/AMDGPU/GlobalISel/inline-asm-lowering-errors.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
; RUN: not llc -mtriple=amdgcn -mcpu=fiji -O0 -global-isel -global-isel-abort=2 -pass-remarks-missed='gisel*' %s -o - 2>&1 | FileCheck %s

; CHECK: error: invalid constraint '': aggregate input operands not supported yet
define amdgpu_kernel void @aggregates([4 x i8] %val) {
tail call void asm sideeffect "s_nop", "r"([4 x i8] %val)
ret void
}

; CHECK: error: invalid constraint '{s999}': could not allocate output register for constraint
define amdgpu_kernel void @bad_output() {
tail call i32 asm sideeffect "s_nop", "={s999}"()
ret void
}

; CHECK: error: invalid constraint '{s998}': could not allocate input register for register constraint
define amdgpu_kernel void @bad_input() {
tail call void asm sideeffect "s_nop", "{s998}"(i32 poison)
ret void
}
; CHECK: error: invalid constraint '{s997}': indirect register inputs are not supported yet
define amdgpu_kernel void @indirect_input() {
tail call void asm sideeffect "s_nop", "*{s997}"(ptr elementtype(i32) poison)
ret void
}

; CHECK: error: invalid constraint 'i': unsupported constraint
define amdgpu_kernel void @badimm() {
tail call void asm sideeffect "s_nop", "i"(i32 poison)
ret void
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
; NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py
; RUN: llc -global-isel -global-isel-abort=2 -pass-remarks-missed='gisel*' -mtriple=amdgcn -mcpu=fiji -stop-after=irtranslator -verify-machineinstrs %s -o - 2>%t | FileCheck %s
; RUN: not llc -global-isel -global-isel-abort=2 -pass-remarks-missed='gisel*' -mtriple=amdgcn -mcpu=fiji -stop-after=irtranslator -verify-machineinstrs %s -o - 2>%t | FileCheck %s
; RUN: FileCheck -check-prefix=ERR %s < %t

; ERR: remark: <unknown>:0:0: unable to translate instruction: call: ' %sgpr = call <4 x i32> asm sideeffect "; def $0", "={s[8:12]}"()' (in function: return_type_is_too_big_vector)
Expand Down
Loading