https://github.com/el-ev updated https://github.com/llvm/llvm-project/pull/154014
>From 53257acd6f023969a0648315410dc1a9ea54abed Mon Sep 17 00:00:00 2001 From: Iris Shi <0...@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 8626ed920b678..e68dacf20854c 100644 --- a/clang/include/clang/CIR/MissingFeatures.h +++ b/clang/include/clang/CIR/MissingFeatures.h @@ -178,9 +178,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)); +} _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits