[llvm-branch-commits] [clang] [CIR] Implement codegen for inline assembly with output operands (PR #154014)
Iris Shi via llvm-branch-commits
llvm-branch-commits at lists.llvm.org
Sun Aug 17 05:06:30 PDT 2025
https://github.com/el-ev updated https://github.com/llvm/llvm-project/pull/154014
>From 775b7a5240c80babb6bff1146ae08521d726740d Mon Sep 17 00:00:00 2001
From: Iris Shi <0.0 at owo.li>
Date: Sun, 17 Aug 2025 20:02:13 +0800
Subject: [PATCH] [CIR] Implement codegen for inline assembly with output
operands
---
clang/include/clang/CIR/MissingFeatures.h | 3 +-
clang/lib/CIR/CodeGen/CIRGenAsm.cpp | 389 +++++++++++++++++++++-
clang/lib/CIR/CodeGen/CIRGenModule.cpp | 20 ++
clang/lib/CIR/CodeGen/CIRGenModule.h | 9 +
clang/lib/CIR/CodeGen/TargetInfo.h | 18 +
clang/test/CIR/CodeGen/inline-asm.c | 38 ++-
6 files changed, 468 insertions(+), 9 deletions(-)
diff --git a/clang/include/clang/CIR/MissingFeatures.h b/clang/include/clang/CIR/MissingFeatures.h
index ebf57246ba0b9..042190201b8f2 100644
--- a/clang/include/clang/CIR/MissingFeatures.h
+++ b/clang/include/clang/CIR/MissingFeatures.h
@@ -179,9 +179,10 @@ struct MissingFeatures {
static bool asmGoto() { return false; }
static bool asmInputOperands() { return false; }
static bool asmLabelAttr() { return false; }
+ static bool asmLLVMAssume() { return false; }
static bool asmMemoryEffects() { return false; }
- static bool asmOutputOperands() { return false; }
static bool asmUnwindClobber() { return false; }
+ static bool asmVectorType() { return false; }
static bool assignMemcpyizer() { return false; }
static bool astVarDeclInterface() { return false; }
static bool attributeBuiltin() { return false; }
diff --git a/clang/lib/CIR/CodeGen/CIRGenAsm.cpp b/clang/lib/CIR/CodeGen/CIRGenAsm.cpp
index 17dffb3515d2a..34e45ff64bea9 100644
--- a/clang/lib/CIR/CodeGen/CIRGenAsm.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenAsm.cpp
@@ -10,6 +10,8 @@
//
//===----------------------------------------------------------------------===//
+#include "clang/Basic/DiagnosticSema.h"
+
#include "CIRGenFunction.h"
#include "clang/CIR/MissingFeatures.h"
@@ -26,6 +28,98 @@ static AsmFlavor inferFlavor(const CIRGenModule &cgm, const AsmStmt &s) {
return isa<MSAsmStmt>(&s) ? AsmFlavor::x86_intel : gnuAsmFlavor;
}
+// FIXME(cir): This should be a common helper between CIRGen
+// and traditional CodeGen
+static std::string simplifyConstraint(
+ const char *constraint, const TargetInfo &target,
+ SmallVectorImpl<TargetInfo::ConstraintInfo> *outCons = nullptr) {
+ std::string result;
+
+ while (*constraint) {
+ switch (*constraint) {
+ default:
+ result += target.convertConstraint(constraint);
+ break;
+ // Ignore these
+ case '*':
+ case '?':
+ case '!':
+ case '=': // Will see this and the following in mult-alt constraints.
+ case '+':
+ break;
+ case '#': // Ignore the rest of the constraint alternative.
+ while (constraint[1] && constraint[1] != ',')
+ constraint++;
+ break;
+ case '&':
+ case '%':
+ result += *constraint;
+ while (constraint[1] && constraint[1] == *constraint)
+ constraint++;
+ break;
+ case ',':
+ result += "|";
+ break;
+ case 'g':
+ result += "imr";
+ break;
+ case '[': {
+ assert(outCons &&
+ "Must pass output names to constraints with a symbolic name");
+ unsigned index;
+ bool resolveResult =
+ target.resolveSymbolicName(constraint, *outCons, index);
+ assert(resolveResult && "Could not resolve symbolic name");
+ (void)resolveResult;
+ result += llvm::utostr(index);
+ break;
+ }
+ }
+
+ constraint++;
+ }
+
+ return result;
+}
+
+// FIXME(cir): This should be a common helper between CIRGen
+// and traditional CodeGen
+/// Look at AsmExpr and if it is a variable declared
+/// as using a particular register add that as a constraint that will be used
+/// in this asm stmt.
+static std::string
+addVariableConstraints(const std::string &constraint, const Expr &asmExpr,
+ const TargetInfo &target, CIRGenModule &cgm,
+ const AsmStmt &stmt, const bool earlyClobber,
+ std::string *gccReg = nullptr) {
+ const DeclRefExpr *asmDeclRef = dyn_cast<DeclRefExpr>(&asmExpr);
+ if (!asmDeclRef)
+ return constraint;
+ const ValueDecl &value = *asmDeclRef->getDecl();
+ const VarDecl *variable = dyn_cast<VarDecl>(&value);
+ if (!variable)
+ return constraint;
+ if (variable->getStorageClass() != SC_Register)
+ return constraint;
+ AsmLabelAttr *attr = variable->getAttr<AsmLabelAttr>();
+ if (!attr)
+ return constraint;
+ StringRef registerName = attr->getLabel();
+ assert(target.isValidGCCRegisterName(registerName));
+ // We're using validateOutputConstraint here because we only care if
+ // this is a register constraint.
+ TargetInfo::ConstraintInfo info(constraint, "");
+ if (target.validateOutputConstraint(info) && !info.allowsRegister()) {
+ cgm.errorUnsupported(&stmt, "__asm__");
+ return constraint;
+ }
+ // Canonicalize the register here before returning it.
+ registerName = target.getNormalizedGCCRegisterName(registerName);
+ if (gccReg != nullptr)
+ *gccReg = registerName.str();
+ return (earlyClobber ? "&{" : "{") + registerName.str() + "}";
+}
+
static void collectClobbers(const CIRGenFunction &cgf, const AsmStmt &s,
std::string &constraints, bool &hasUnwindClobber,
bool &readOnly, bool readNone) {
@@ -83,16 +177,150 @@ static void collectClobbers(const CIRGenFunction &cgf, const AsmStmt &s,
}
}
+using ConstraintInfos = SmallVector<TargetInfo::ConstraintInfo, 4>;
+
+static void collectInOutConstraintInfos(const CIRGenFunction &cgf,
+ const AsmStmt &s, ConstraintInfos &out,
+ ConstraintInfos &in) {
+
+ for (unsigned i = 0, e = s.getNumOutputs(); i != e; i++) {
+ StringRef name;
+ if (const GCCAsmStmt *gas = dyn_cast<GCCAsmStmt>(&s))
+ name = gas->getOutputName(i);
+ TargetInfo::ConstraintInfo info(s.getOutputConstraint(i), name);
+ bool isValid = cgf.getTarget().validateOutputConstraint(info);
+ (void)isValid;
+ assert(isValid && "Failed to parse output constraint");
+ out.push_back(info);
+ }
+
+ for (unsigned i = 0, e = s.getNumInputs(); i != e; i++) {
+ StringRef name;
+ if (const GCCAsmStmt *gas = dyn_cast<GCCAsmStmt>(&s))
+ name = gas->getInputName(i);
+ TargetInfo::ConstraintInfo info(s.getInputConstraint(i), name);
+ bool isValid = cgf.getTarget().validateInputConstraint(out, info);
+ assert(isValid && "Failed to parse input constraint");
+ (void)isValid;
+ in.push_back(info);
+ }
+}
+
+static void emitAsmStores(CIRGenFunction &cgf, const AsmStmt &s,
+ const llvm::ArrayRef<mlir::Value> regResults,
+ const llvm::ArrayRef<mlir::Type> resultRegTypes,
+ const llvm::ArrayRef<mlir::Type> resultTruncRegTypes,
+ const llvm::ArrayRef<LValue> resultRegDests,
+ const llvm::ArrayRef<QualType> resultRegQualTys,
+ const llvm::BitVector &resultTypeRequiresCast,
+ const llvm::BitVector &resultRegIsFlagReg) {
+ CIRGenBuilderTy &builder = cgf.getBuilder();
+ CIRGenModule &cgm = cgf.cgm;
+ mlir::MLIRContext *ctx = builder.getContext();
+
+ assert(regResults.size() == resultRegTypes.size());
+ assert(regResults.size() == resultTruncRegTypes.size());
+ assert(regResults.size() == resultRegDests.size());
+
+ // ResultRegDests can be also populated by addReturnRegisterOutputs() above,
+ // in which case its size may grow.
+ assert(resultTypeRequiresCast.size() <= resultRegDests.size());
+ assert(resultRegIsFlagReg.size() <= resultRegDests.size());
+
+ for (unsigned i = 0, e = regResults.size(); i != e; ++i) {
+ mlir::Value tmp = regResults[i];
+ mlir::Type truncTy = resultTruncRegTypes[i];
+
+ if (i < resultRegIsFlagReg.size() && resultRegIsFlagReg[i])
+ assert(!cir::MissingFeatures::asmLLVMAssume());
+
+ // 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) {
+
+ // Truncate the integer result to the right size, note that TruncTy can be
+ // a pointer.
+ if (mlir::isa<mlir::FloatType>(truncTy)) {
+ tmp = builder.createFloatingCast(tmp, truncTy);
+ } else if (isa<cir::PointerType>(truncTy) &&
+ isa<cir::IntType>(tmp.getType())) {
+ uint64_t resSize = cgm.getDataLayout().getTypeSizeInBits(truncTy);
+ tmp = builder.createIntCast(
+ tmp, cir::IntType::get(ctx, (unsigned)resSize, false));
+ tmp = builder.createIntToPtr(tmp, truncTy);
+ } else if (isa<cir::PointerType>(tmp.getType()) &&
+ isa<cir::IntType>(truncTy)) {
+ uint64_t tmpSize = cgm.getDataLayout().getTypeSizeInBits(tmp.getType());
+ tmp = builder.createPtrToInt(
+ tmp, cir::IntType::get(ctx, (unsigned)tmpSize, false));
+ tmp = builder.createIntCast(tmp, truncTy);
+ } else if (isa<cir::IntType>(truncTy)) {
+ tmp = builder.createIntCast(tmp, truncTy);
+ } else if (isa<cir::VectorType>(truncTy)) {
+ assert(!cir::MissingFeatures::asmVectorType());
+ }
+ }
+
+ LValue dest = resultRegDests[i];
+ // ResultTypeRequiresCast elements correspond to the first
+ // ResultTypeRequiresCast.size() elements of RegResults.
+ if ((i < resultTypeRequiresCast.size()) && resultTypeRequiresCast[i]) {
+ unsigned size = cgf.getContext().getTypeSize(resultRegQualTys[i]);
+ Address addr =
+ dest.getAddress().withElementType(builder, resultRegTypes[i]);
+ if (cgm.getTargetCIRGenInfo().isScalarizableAsmOperand(cgf, truncTy)) {
+ builder.createStore(cgf.getLoc(s.getAsmLoc()), tmp, addr);
+ continue;
+ }
+
+ QualType ty =
+ cgf.getContext().getIntTypeForBitwidth(size, /*Signed=*/false);
+ if (ty.isNull()) {
+ const Expr *outExpr = s.getOutputExpr(i);
+ cgm.getDiags().Report(outExpr->getExprLoc(),
+ diag::err_store_value_to_reg);
+ return;
+ }
+ dest = cgf.makeAddrLValue(addr, ty);
+ }
+
+ cgf.emitStoreThroughLValue(RValue::get(tmp), dest);
+ }
+}
+
mlir::LogicalResult CIRGenFunction::emitAsmStmt(const AsmStmt &s) {
// Assemble the final asm string.
std::string asmString = s.generateAsmString(getContext());
+ SourceLocation srcLoc = s.getAsmLoc();
+ mlir::Location loc = getLoc(srcLoc);
+
+ // Get all the output and input constraints together.
+ ConstraintInfos outputConstraintInfos;
+ ConstraintInfos inputConstraintInfos;
+ collectInOutConstraintInfos(*this, s, outputConstraintInfos,
+ inputConstraintInfos);
bool isGCCAsmGoto = false;
std::string constraints;
+ std::vector<LValue> resultRegDests;
+ std::vector<QualType> resultRegQualTys;
+ std::vector<mlir::Type> resultRegTypes;
+ std::vector<mlir::Type> resultTruncRegTypes;
+ std::vector<mlir::Type> argTypes;
+ std::vector<mlir::Type> argElemTypes;
+ std::vector<mlir::Value> args;
std::vector<mlir::Value> outArgs;
std::vector<mlir::Value> inArgs;
std::vector<mlir::Value> inOutArgs;
+ llvm::BitVector resultTypeRequiresCast;
+ llvm::BitVector resultRegIsFlagReg;
+
+ // Keep track of out constraints for tied input operand.
+ std::vector<std::string> outputConstraints;
+
+ // Keep track of defined physregs.
+ llvm::SmallSet<std::string, 8> physRegOutputs;
// An inline asm can be marked readonly if it meets the following conditions:
// - it doesn't have any sideeffects
@@ -102,12 +330,104 @@ mlir::LogicalResult CIRGenFunction::emitAsmStmt(const AsmStmt &s) {
// in addition to meeting the conditions listed above.
bool readOnly = true, readNone = true;
- if (s.getNumInputs() != 0 || s.getNumOutputs() != 0) {
+ if (s.getNumInputs() != 0) {
assert(!cir::MissingFeatures::asmInputOperands());
- assert(!cir::MissingFeatures::asmOutputOperands());
- cgm.errorNYI(s.getAsmLoc(), "asm with operands");
+ cgm.errorNYI(srcLoc, "asm with input operands");
}
+ for (unsigned i = 0, e = s.getNumOutputs(); i != e; i++) {
+ TargetInfo::ConstraintInfo &info = outputConstraintInfos[i];
+
+ // Simplify the output constraint.
+ std::string outputConstraint(s.getOutputConstraint(i));
+ outputConstraint =
+ simplifyConstraint(outputConstraint.c_str() + 1, getTarget());
+
+ const Expr *outExpr = s.getOutputExpr(i);
+ outExpr = outExpr->IgnoreParenNoopCasts(getContext());
+
+ std::string gccReg;
+ outputConstraint =
+ addVariableConstraints(outputConstraint, *outExpr, getTarget(), cgm, s,
+ info.earlyClobber(), &gccReg);
+
+ // Give an error on multiple outputs to same physreg.
+ if (!gccReg.empty() && !physRegOutputs.insert(gccReg).second)
+ cgm.error(srcLoc, "multiple outputs to hard register: " + gccReg);
+
+ outputConstraints.push_back(outputConstraint);
+ LValue dest = emitLValue(outExpr);
+
+ if (!constraints.empty())
+ constraints += ',';
+
+ // If this is a register output, then make the inline a sm return it
+ // by-value. If this is a memory result, return the value by-reference.
+ QualType qty = outExpr->getType();
+ const bool isScalarOrAggregate =
+ hasScalarEvaluationKind(qty) || hasAggregateEvaluationKind(qty);
+ if (!info.allowsMemory() && isScalarOrAggregate) {
+ constraints += "=" + outputConstraint;
+ resultRegQualTys.push_back(qty);
+ resultRegDests.push_back(dest);
+
+ bool isFlagReg = llvm::StringRef(outputConstraint).starts_with("{@cc");
+ resultRegIsFlagReg.push_back(isFlagReg);
+
+ mlir::Type ty = convertTypeForMem(qty);
+ const bool requiresCast =
+ info.allowsRegister() &&
+ (cgm.getTargetCIRGenInfo().isScalarizableAsmOperand(*this, ty) ||
+ isa<cir::RecordType, cir::ArrayType>(ty));
+
+ resultTruncRegTypes.push_back(ty);
+ resultTypeRequiresCast.push_back(requiresCast);
+
+ if (requiresCast) {
+ unsigned size = getContext().getTypeSize(qty);
+ ty = cir::IntType::get(&getMLIRContext(), size, false);
+ }
+
+ resultRegTypes.push_back(ty);
+
+ if (info.hasMatchingInput())
+ assert(!cir::MissingFeatures::asmInputOperands());
+
+ if (mlir::Type adjTy = cgm.getTargetCIRGenInfo().adjustInlineAsmType(
+ *this, outputConstraint, resultRegTypes.back()))
+ resultRegTypes.back() = adjTy;
+ else
+ cgm.getDiags().Report(srcLoc, diag::err_asm_invalid_type_in_input)
+ << outExpr->getType() << outputConstraint;
+
+ // Update largest vector width for any vector types.
+ assert(!cir::MissingFeatures::asmVectorType());
+ } else {
+ Address destAddr = dest.getAddress();
+
+ // Matrix types in memory are represented by arrays, but accessed through
+ // vector pointers, with the alignment specified on the access operation.
+ // For inline assembly, update pointer arguments to use vector pointers.
+ // Otherwise there will be a mis-match if the matrix is also an
+ // input-argument which is represented as vector.
+ if (isa<MatrixType>(outExpr->getType().getCanonicalType()))
+ destAddr =
+ destAddr.withElementType(builder, convertType(outExpr->getType()));
+
+ argTypes.push_back(destAddr.getType());
+ argElemTypes.push_back(destAddr.getElementType());
+ outArgs.push_back(destAddr.getPointer());
+ args.push_back(destAddr.getPointer());
+ constraints += "=*";
+ constraints += outputConstraint;
+ readOnly = readNone = false;
+ }
+
+ if (info.isReadWrite())
+ assert(!cir::MissingFeatures::asmInputOperands());
+
+ } // iterate over output operands
+
bool hasUnwindClobber = false;
collectClobbers(*this, s, constraints, hasUnwindClobber, readOnly, readNone);
@@ -115,11 +435,20 @@ mlir::LogicalResult CIRGenFunction::emitAsmStmt(const AsmStmt &s) {
mlir::Type resultType;
+ if (resultRegTypes.size() == 1) {
+ resultType = resultRegTypes[0];
+ } else if (resultRegTypes.size() > 1) {
+ std::string sname = builder.getUniqueAnonRecordName();
+ resultType =
+ builder.getCompleteRecordTy(resultRegTypes, sname, false, false);
+ }
bool hasSideEffect = s.isVolatile() || s.getNumOutputs() == 0;
+ std::vector<mlir::Value> regResults;
+
cir::InlineAsmOp ia = builder.create<cir::InlineAsmOp>(
- getLoc(s.getAsmLoc()), resultType, operands, asmString, constraints,
- hasSideEffect, inferFlavor(cgm, s), mlir::ArrayAttr());
+ loc, resultType, operands, asmString, constraints, hasSideEffect,
+ inferFlavor(cgm, s), mlir::ArrayAttr());
if (isGCCAsmGoto) {
assert(!cir::MissingFeatures::asmGoto());
@@ -127,10 +456,56 @@ mlir::LogicalResult CIRGenFunction::emitAsmStmt(const AsmStmt &s) {
assert(!cir::MissingFeatures::asmUnwindClobber());
} else {
assert(!cir::MissingFeatures::asmMemoryEffects());
+
+ mlir::Value result;
+ if (ia.getNumResults())
+ result = ia.getResult(0);
+
+ llvm::SmallVector<mlir::Attribute> operandAttrs;
+
+ int i = 0;
+ for (auto typ : argElemTypes) {
+ if (typ) {
+ auto op = args[i++];
+ assert(mlir::isa<cir::PointerType>(op.getType()) &&
+ "pointer type expected");
+ assert(cast<cir::PointerType>(op.getType()).getPointee() == typ &&
+ "element type differs from pointee type!");
+
+ operandAttrs.push_back(mlir::UnitAttr::get(&getMLIRContext()));
+ } else {
+ // We need to add an attribute for every arg since later, during
+ // the lowering to LLVM IR the attributes will be assigned to the
+ // CallInsn argument by index, i.e. we can't skip null type here
+ operandAttrs.push_back(mlir::Attribute());
+ }
+ }
+ assert(args.size() == operandAttrs.size() &&
+ "The number of attributes is not even with the number of operands");
+
+ ia.setOperandAttrsAttr(builder.getArrayAttr(operandAttrs));
+
+ if (resultRegTypes.size() == 1) {
+ regResults.push_back(result);
+ } else if (resultRegTypes.size() > 1) {
+ CharUnits alignment = CharUnits::One();
+ mlir::StringAttr sname = cast<cir::RecordType>(resultType).getName();
+ mlir::Value dest = emitAlloca(sname, resultType, loc, alignment, false);
+ Address addr = Address(dest, alignment);
+ builder.createStore(loc, result, addr);
+
+ for (unsigned i = 0, e = resultRegTypes.size(); i != e; ++i) {
+ cir::PointerType typ = builder.getPointerTo(resultRegTypes[i]);
+ cir::GetMemberOp ptr = builder.createGetMember(loc, typ, dest, "", i);
+ cir::LoadOp tmp = builder.createLoad(loc, Address(ptr, alignment));
+ regResults.push_back(tmp);
+ }
+ }
}
- llvm::SmallVector<mlir::Attribute> operandAttrs;
- ia.setOperandAttrsAttr(builder.getArrayAttr(operandAttrs));
+ emitAsmStores(*this, s, regResults, resultRegTypes, resultTruncRegTypes,
+ resultRegDests, resultRegQualTys, resultTypeRequiresCast,
+ resultRegIsFlagReg);
return mlir::success();
}
diff --git a/clang/lib/CIR/CodeGen/CIRGenModule.cpp b/clang/lib/CIR/CodeGen/CIRGenModule.cpp
index d5296881540aa..8e1579810e147 100644
--- a/clang/lib/CIR/CodeGen/CIRGenModule.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenModule.cpp
@@ -2158,3 +2158,23 @@ DiagnosticBuilder CIRGenModule::errorNYI(SourceRange loc,
llvm::StringRef feature) {
return errorNYI(loc.getBegin(), feature) << loc;
}
+
+void CIRGenModule::error(SourceLocation loc, StringRef error) {
+ unsigned diagID = getDiags().getCustomDiagID(DiagnosticsEngine::Error, "%0");
+ getDiags().Report(astContext.getFullLoc(loc), diagID) << error;
+}
+
+/// Print out an error that codegen doesn't support the specified stmt yet.
+void CIRGenModule::errorUnsupported(const Stmt *s, llvm::StringRef type) {
+ unsigned diagId = diags.getCustomDiagID(DiagnosticsEngine::Error,
+ "cannot compile this %0 yet");
+ diags.Report(astContext.getFullLoc(s->getBeginLoc()), diagId)
+ << type << s->getSourceRange();
+}
+
+/// Print out an error that codegen doesn't support the specified decl yet.
+void CIRGenModule::errorUnsupported(const Decl *d, llvm::StringRef type) {
+ unsigned diagId = diags.getCustomDiagID(DiagnosticsEngine::Error,
+ "cannot compile this %0 yet");
+ diags.Report(astContext.getFullLoc(d->getLocation()), diagId) << type;
+}
diff --git a/clang/lib/CIR/CodeGen/CIRGenModule.h b/clang/lib/CIR/CodeGen/CIRGenModule.h
index 06cc3e09e416b..e8fd8fcb59689 100644
--- a/clang/lib/CIR/CodeGen/CIRGenModule.h
+++ b/clang/lib/CIR/CodeGen/CIRGenModule.h
@@ -437,6 +437,15 @@ class CIRGenModule : public CIRGenTypeCache {
return errorNYI(loc.getBegin(), feature, name) << loc;
}
+ /// Emit a general error that something can't be done.
+ void error(SourceLocation loc, llvm::StringRef error);
+
+ /// Print out an error that codegen doesn't support the specified stmt yet.
+ void errorUnsupported(const Stmt *s, llvm::StringRef type);
+
+ /// Print out an error that codegen doesn't support the specified decl yet.
+ void errorUnsupported(const Decl *d, llvm::StringRef type);
+
private:
// An ordered map of canonical GlobalDecls to their mangled names.
llvm::MapVector<clang::GlobalDecl, llvm::StringRef> mangledDeclNames;
diff --git a/clang/lib/CIR/CodeGen/TargetInfo.h b/clang/lib/CIR/CodeGen/TargetInfo.h
index a5c548aa2c7c4..8e8de01a5ed87 100644
--- a/clang/lib/CIR/CodeGen/TargetInfo.h
+++ b/clang/lib/CIR/CodeGen/TargetInfo.h
@@ -32,6 +32,8 @@ bool isEmptyFieldForLayout(const ASTContext &context, const FieldDecl *fd);
/// if the [[no_unique_address]] attribute would have made them empty.
bool isEmptyRecordForLayout(const ASTContext &context, QualType t);
+class CIRGenFunction;
+
class TargetCIRGenInfo {
std::unique_ptr<ABIInfo> info;
@@ -86,6 +88,22 @@ class TargetCIRGenInfo {
/// may need to adjust the debugger-support code in Sema to do the
/// right thing when calling a function with no know signature.
virtual bool isNoProtoCallVariadic(const FunctionNoProtoType *fnType) const;
+
+ virtual bool isScalarizableAsmOperand(CIRGenFunction &cgf,
+ mlir::Type ty) const {
+ return false;
+ }
+
+ /// Corrects the MLIR type for a given constraint and "usual"
+ /// type.
+ ///
+ /// \returns A new MLIR type, possibly the same as the original
+ /// on success
+ virtual mlir::Type adjustInlineAsmType(CIRGenFunction &cgf,
+ llvm::StringRef constraint,
+ mlir::Type ty) const {
+ return ty;
+ }
};
std::unique_ptr<TargetCIRGenInfo> createX8664TargetCIRGenInfo(CIRGenTypes &cgt);
diff --git a/clang/test/CIR/CodeGen/inline-asm.c b/clang/test/CIR/CodeGen/inline-asm.c
index fc959f9326876..43677e0f138a4 100644
--- a/clang/test/CIR/CodeGen/inline-asm.c
+++ b/clang/test/CIR/CodeGen/inline-asm.c
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -Wno-unused-value -fclangir -emit-cir %s -o %t.cir
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir %s -o %t.cir
// RUN: FileCheck --input-file=%t.cir %s -check-prefix=CIR
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-llvm %s -o %t-cir.ll
// RUN: FileCheck --input-file=%t-cir.ll %s --check-prefix=LLVM
@@ -22,3 +22,39 @@ void f2() {
// LLVM: call void asm sideeffect "nop", "~{dirflag},~{fpsr},~{flags}"()
__asm__ volatile("nop" : : : );
}
+
+// CIR: %[[X:.*]] = cir.alloca !s32i, !cir.ptr<!s32i>, ["x", init] {alignment = 4 : i64}
+// CIR: cir.asm(x86_att,
+// CIR: out = [%[[X]] : !cir.ptr<!s32i> (maybe_memory)],
+// CIR: in = [],
+// CIR: in_out = [],
+// CIR: {"" "=*m,~{dirflag},~{fpsr},~{flags}"}) side_effects
+void f3(int x) {
+ __asm__ volatile("" : "=m"(x));
+}
+
+// CIR: %[[A:.*]] = cir.alloca !s32i, !cir.ptr<!s32i>, ["a"] {alignment = 4 : i64}
+// CIR: %[[RES:.*]] = cir.asm(x86_att,
+// CIR: out = [],
+// CIR: in = [],
+// CIR: in_out = [],
+// CIR: {"movl $$42, $0" "=r,~{dirflag},~{fpsr},~{flags}"}) -> !s32i
+// CIR: cir.store align(4) %[[RES]], %[[A]] : !s32i, !cir.ptr<!s32i>
+unsigned f4(unsigned x) {
+ int a;
+ __asm__("movl $42, %0" : "=r" (a) : );
+ return a;
+}
+
+// CIR: [[TMP0:%.*]] = cir.alloca !cir.ptr<!s32i>, !cir.ptr<!cir.ptr<!s32i>>, ["x", init]
+// CIR: cir.store{{.*}} %arg0, [[TMP0]] : !cir.ptr<!s32i>, !cir.ptr<!cir.ptr<!s32i>>
+// CIR: [[TMP1:%.*]] = cir.load deref{{.*}} [[TMP0]] : !cir.ptr<!cir.ptr<!s32i>>, !cir.ptr<!s32i>
+// CIR: cir.asm(x86_att,
+// CIR: out = [%1 : !cir.ptr<!s32i> (maybe_memory)],
+// CIR: in = [],
+// CIR: in_out = [],
+// CIR: {"addl $$42, $0" "=*m,~{dirflag},~{fpsr},~{flags}"})
+// CIR-NEXT: cir.return
+void f5(int *x) {
+ __asm__("addl $42, %[addr]" : [addr] "=m" (*x));
+}
More information about the llvm-branch-commits
mailing list