From a5a32a00acc2e66a264afbb8fa4efcce337eadde Mon Sep 17 00:00:00 2001 From: Iris Shi <0.0@owo.li> Date: Wed, 13 Aug 2025 13:18:57 +0800 Subject: [PATCH 01/12] [CIR] Add `InlineAsmOp` --- clang/include/clang/CIR/Dialect/IR/CIROps.td | 83 ++++++++ clang/lib/CIR/Dialect/IR/CIRDialect.cpp | 202 +++++++++++++++++++ 2 files changed, 285 insertions(+) diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td b/clang/include/clang/CIR/Dialect/IR/CIROps.td index b64fd2734a63c..08afd2ddb1b80 100644 --- a/clang/include/clang/CIR/Dialect/IR/CIROps.td +++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td @@ -2254,6 +2254,89 @@ def CIR_StackRestoreOp : CIR_Op<"stackrestore"> { let assemblyFormat = "$ptr attr-dict `:` qualified(type($ptr))"; } +//===----------------------------------------------------------------------===// +// InlineAsmOp +//===----------------------------------------------------------------------===// + +def CIR_AsmFlavor : CIR_I32EnumAttr<"AsmFlavor", "ATT or Intel", + [I32EnumAttrCase<"x86_att", 0>, + I32EnumAttrCase<"x86_intel", 1>]>; + +def CIR_InlineAsmOp : CIR_Op<"asm", [RecursiveMemoryEffects]> { + let description = [{ + The `cir.asm` operation represents C/C++ asm inline. + + CIR constraints strings follow barely the same rules that are established + for the C level assembler constraints with several differences caused by + clang::AsmStmt processing. + + Thus, numbers that appears in the constraint string may also refer to: + - the output variable index referenced by the input operands. + - the index of early-clobber operand + + Operand attributes are a storage, where each element corresponds to the + operand with the same index. The first index relates to the operation + result (if any). + Note, the operands themselves are stored as VariadicOfVariadic in the next + order: output, input and then in/out operands. + + Note, when several output operands are present, the result type may be + represented as an anon record type. + + Example: + ```C++ + __asm__("foo" : : : ); + __asm__("bar $42 %[val]" : [val] "=r" (x), "+&r"(x)); + __asm__("baz $42 %[val]" : [val] "=r" (x), "+&r"(x) : "[val]"(y)); + ``` + + ```mlir + !rec_22anon2E022 = !cir.record, !cir.int}> + !rec_22anon2E122 = !cir.record, !cir.int}> + ... + %0 = cir.alloca !s32i, !cir.ptr, ["x", init] + %1 = cir.alloca !s32i, !cir.ptr, ["y", init] + ... + %2 = cir.load %0 : !cir.ptr, !s32i + %3 = cir.load %1 : !cir.ptr, !s32i + + cir.asm(x86_att, + out = [], + in = [], + in_out = [], + {"foo" "~{dirflag},~{fpsr},~{flags}"}) side_effects + + cir.asm(x86_att, + out = [], + in = [], + in_out = [%2 : !s32i], + {"bar $$42 $0" "=r,=&r,1,~{dirflag},~{fpsr},~{flags}"}) -> !rec_22anon2E022 + + cir.asm(x86_att, + out = [], + in = [%3 : !s32i], + in_out = [%2 : !s32i], + {"baz $$42 $0" "=r,=&r,0,1,~{dirflag},~{fpsr},~{flags}"}) -> !rec_22anon2E122 + ``` + }]; + + let results = (outs Optional:$res); + + let arguments = + (ins VariadicOfVariadic:$asm_operands, + StrAttr:$asm_string, StrAttr:$constraints, UnitAttr:$side_effects, + CIR_AsmFlavor:$asm_flavor, ArrayAttr:$operand_attrs, + DenseI32ArrayAttr:$operands_segments); + + let builders = [OpBuilder<(ins + "llvm::ArrayRef":$asmOperands, + "llvm::StringRef":$asmString, "llvm::StringRef":$constraints, + "bool":$sideEffects, "AsmFlavor":$asmFlavor, + "llvm::ArrayRef":$operandAttrs)>]; + + let hasCustomAssemblyFormat = 1; +} + //===----------------------------------------------------------------------===// // UnreachableOp //===----------------------------------------------------------------------===// diff --git a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp index 936247e9d8fbb..2404755cde1e0 100644 --- a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp +++ b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp @@ -2419,6 +2419,208 @@ OpFoldResult RotateOp::fold(FoldAdaptor adaptor) { return IntAttr::get(input.getContext(), input.getType(), resultValue); } +//===----------------------------------------------------------------------===// +// InlineAsmOp +//===----------------------------------------------------------------------===// + +void cir::InlineAsmOp::print(OpAsmPrinter &p) { + p << '(' << getAsmFlavor() << ", "; + p.increaseIndent(); + p.printNewline(); + + llvm::SmallVector names{"out", "in", "in_out"}; + auto *nameIt = names.begin(); + auto *attrIt = getOperandAttrs().begin(); + + for (auto ops : getAsmOperands()) { + p << *nameIt << " = "; + + p << '['; + llvm::interleaveComma(llvm::make_range(ops.begin(), ops.end()), p, + [&](Value value) { + p.printOperand(value); + p << " : " << value.getType(); + if (*attrIt) + p << " (maybe_memory)"; + attrIt++; + }); + p << "],"; + p.printNewline(); + ++nameIt; + } + + p << "{"; + p.printString(getAsmString()); + p << " "; + p.printString(getConstraints()); + p << "}"; + p.decreaseIndent(); + p << ')'; + if (getSideEffects()) + p << " side_effects"; + + llvm::SmallVector<::llvm::StringRef, 2> elidedAttrs; + elidedAttrs.push_back("asm_flavor"); + elidedAttrs.push_back("asm_string"); + elidedAttrs.push_back("constraints"); + elidedAttrs.push_back("operand_attrs"); + elidedAttrs.push_back("operands_segments"); + elidedAttrs.push_back("side_effects"); + p.printOptionalAttrDict(getOperation()->getAttrs(), elidedAttrs); + + if (auto v = getRes()) + p << " -> " << v.getType(); +} + +void cir::InlineAsmOp::build(OpBuilder &odsBuilder, OperationState &odsState, + ArrayRef asmOperands, + StringRef asmString, StringRef constraints, + bool sideEffects, cir::AsmFlavor asmFlavor, + ArrayRef operandAttrs) { + // Set up the operands_segments for VariadicOfVariadic + SmallVector segments; + for (auto operandRange : asmOperands) { + segments.push_back(operandRange.size()); + odsState.addOperands(operandRange); + } + + odsState.addAttribute( + "operands_segments", + DenseI32ArrayAttr::get(odsBuilder.getContext(), segments)); + odsState.addAttribute("asm_string", odsBuilder.getStringAttr(asmString)); + odsState.addAttribute("constraints", odsBuilder.getStringAttr(constraints)); + odsState.addAttribute("asm_flavor", + AsmFlavorAttr::get(odsBuilder.getContext(), asmFlavor)); + + if (sideEffects) + odsState.addAttribute("side_effects", odsBuilder.getUnitAttr()); + + odsState.addAttribute("operand_attrs", odsBuilder.getArrayAttr(operandAttrs)); +} + +ParseResult cir::InlineAsmOp::parse(OpAsmParser &parser, + OperationState &result) { + llvm::SmallVector operandAttrs; + llvm::SmallVector operandsGroupSizes; + std::string asmString, constraints; + Type resType; + auto *ctxt = parser.getBuilder().getContext(); + + auto error = [&](const Twine &msg) -> LogicalResult { + return parser.emitError(parser.getCurrentLocation(), msg); + }; + + auto expected = [&](const std::string &c) { + return error("expected '" + c + "'"); + }; + + if (parser.parseLParen().failed()) + return expected("("); + + auto flavor = FieldParser::parse(parser); + if (failed(flavor)) + return error("Unknown AsmFlavor"); + + if (parser.parseComma().failed()) + return expected(","); + + auto parseValue = [&](Value &v) { + OpAsmParser::UnresolvedOperand op; + + if (parser.parseOperand(op) || parser.parseColon()) + return mlir::failure(); + + Type typ; + if (parser.parseType(typ).failed()) + return error("can't parse operand type"); + llvm::SmallVector tmp; + if (parser.resolveOperand(op, typ, tmp)) + return error("can't resolve operand"); + v = tmp[0]; + return mlir::success(); + }; + + auto parseOperands = [&](llvm::StringRef name) { + if (parser.parseKeyword(name).failed()) + return error("expected " + name + " operands here"); + if (parser.parseEqual().failed()) + return expected("="); + if (parser.parseLSquare().failed()) + return expected("["); + + int size = 0; + if (parser.parseOptionalRSquare().succeeded()) { + operandsGroupSizes.push_back(size); + if (parser.parseComma()) + return expected(","); + return mlir::success(); + } + + if (parser.parseCommaSeparatedList([&]() { + Value val; + if (parseValue(val).succeeded()) { + result.operands.push_back(val); + size++; + + if (parser.parseOptionalLParen().failed()) { + operandAttrs.push_back(mlir::Attribute()); + return mlir::success(); + } + + if (parser.parseKeyword("maybe_memory").succeeded()) { + operandAttrs.push_back(mlir::UnitAttr::get(ctxt)); + if (parser.parseRParen()) + return expected(")"); + return mlir::success(); + } + } + return mlir::failure(); + })) + return mlir::failure(); + + if (parser.parseRSquare().failed() || parser.parseComma().failed()) + return expected("]"); + operandsGroupSizes.push_back(size); + return mlir::success(); + }; + + if (parseOperands("out").failed() || parseOperands("in").failed() || + parseOperands("in_out").failed()) + return error("failed to parse operands"); + + if (parser.parseLBrace()) + return expected("{"); + if (parser.parseString(&asmString)) + return error("asm string parsing failed"); + if (parser.parseString(&constraints)) + return error("constraints string parsing failed"); + if (parser.parseRBrace()) + return expected("}"); + if (parser.parseRParen()) + return expected(")"); + + if (parser.parseOptionalKeyword("side_effects").succeeded()) + result.attributes.set("side_effects", UnitAttr::get(ctxt)); + + if (parser.parseOptionalArrow().succeeded() && + parser.parseType(resType).failed()) + return mlir::failure(); + + if (parser.parseOptionalAttrDict(result.attributes)) + return mlir::failure(); + + result.attributes.set("asm_flavor", AsmFlavorAttr::get(ctxt, *flavor)); + result.attributes.set("asm_string", StringAttr::get(ctxt, asmString)); + result.attributes.set("constraints", StringAttr::get(ctxt, constraints)); + result.attributes.set("operand_attrs", ArrayAttr::get(ctxt, operandAttrs)); + result.getOrAddProperties().operands_segments = + parser.getBuilder().getDenseI32ArrayAttr(operandsGroupSizes); + if (resType) + result.addTypes(TypeRange{resType}); + + return mlir::success(); +} + //===----------------------------------------------------------------------===// // TableGen'd op method definitions //===----------------------------------------------------------------------===// From 9e595741e4529e6050c3cdf78f84d41478e85947 Mon Sep 17 00:00:00 2001 From: Iris Shi <0.0@owo.li> Date: Wed, 13 Aug 2025 15:51:03 +0800 Subject: [PATCH 02/12] add parsing test --- clang/test/CIR/IR/inline-asm.cir | 112 +++++++++++++++++++++++++++++++ 1 file changed, 112 insertions(+) create mode 100644 clang/test/CIR/IR/inline-asm.cir diff --git a/clang/test/CIR/IR/inline-asm.cir b/clang/test/CIR/IR/inline-asm.cir new file mode 100644 index 0000000000000..fb1f6315a7d72 --- /dev/null +++ b/clang/test/CIR/IR/inline-asm.cir @@ -0,0 +1,112 @@ +// RUN: cir-opt %s | FileCheck %s + +!s32i = !cir.int +!u32i = !cir.int + +module { +cir.func @f1() { + // CHECK: cir.asm(x86_att, + // CHECK: out = [], + // CHECK: in = [], + // CHECK: in_out = [], + // CHECK: {"" "~{dirflag},~{fpsr},~{flags}"}) + cir.asm(x86_att, + out = [], + in = [], + in_out = [], + {"" "~{dirflag},~{fpsr},~{flags}"}) + cir.return +} + +cir.func @f2() { + // CHECK: cir.asm(x86_att, + // CHECK: out = [], + // CHECK: in = [], + // CHECK: in_out = [], + // CHECK: {"" "~{dirflag},~{fpsr},~{flags}"}) side_effects + cir.asm(x86_att, + out = [], + in = [], + in_out = [], + {"" "~{dirflag},~{fpsr},~{flags}"}) side_effects + cir.return +} + +cir.func @f3() { + // CHECK: cir.asm(x86_att, + // CHECK: out = [], + // CHECK: in = [], + // CHECK: in_out = [], + // CHECK: {"abc" "~{dirflag},~{fpsr},~{flags}"}) side_effects + cir.asm(x86_att, + out = [], + in = [], + in_out = [], + {"abc" "~{dirflag},~{fpsr},~{flags}"}) side_effects + cir.return +} + +cir.func @f4(%arg0: !s32i) { + %0 = cir.alloca !s32i, !cir.ptr, ["x", init] {alignment = 4 : i64} + cir.store %arg0, %0 : !s32i, !cir.ptr + // CHECK: cir.asm(x86_att, + // CHECK: out = [], + // CHECK: in = [%0 : !cir.ptr (maybe_memory)], + // CHECK: in_out = [], + // CHECK: {"" "*m,~{dirflag},~{fpsr},~{flags}"}) side_effects + cir.asm(x86_att, + out = [], + in = [%0 : !cir.ptr (maybe_memory)], + in_out = [], + {"" "*m,~{dirflag},~{fpsr},~{flags}"}) side_effects + cir.return +} + +cir.func @f5() { + // CHECK: cir.asm(x86_intel, + // CHECK: out = [], + // CHECK: in = [], + // CHECK: in_out = [], + // CHECK: {"" "~{dirflag},~{fpsr},~{flags}"}) + cir.asm(x86_intel, + out = [], + in = [], + in_out = [], + {"" "~{dirflag},~{fpsr},~{flags}"}) + cir.return +} +cir.func @f6() -> !s32i { + %0 = cir.alloca !s32i, !cir.ptr, ["x", init] {alignment = 4 : i64} + // CHECK: %1 = cir.asm(x86_att, + // CHECK: out = [], + // CHECK: in = [], + // CHECK: in_out = [], + // CHECK: {"movl $$42, $0" "=r,~{dirflag},~{fpsr},~{flags}"}) side_effects -> !s32i + %1 = cir.asm(x86_att, + out = [], + in = [], + in_out = [], + {"movl $$42, $0" "=r,~{dirflag},~{fpsr},~{flags}"}) side_effects -> !s32i + cir.store align(4) %1, %0 : !s32i, !cir.ptr + %3 = cir.load align(4) %0 : !cir.ptr, !s32i + cir.return %3 : !s32i +} +cir.func @f7(%arg0: !u32i) -> !u32i { + %0 = cir.alloca !u32i, !cir.ptr, ["x", init] {alignment = 4 : i64} + cir.store %arg0, %0 : !u32i, !cir.ptr + %1 = cir.load align(4) %0 : !cir.ptr, !u32i + // CHECK: %2 = cir.asm(x86_att, + // CHECK: out = [], + // CHECK: in = [], + // CHECK: in_out = [%1 : !u32i], + // CHECK: {"addl $$42, $0" "=r,0,~{dirflag},~{fpsr},~{flags}"}) side_effects -> !u32i + %2 = cir.asm(x86_att, + out = [], + in = [], + in_out = [%1 : !u32i], + {"addl $$42, $0" "=r,0,~{dirflag},~{fpsr},~{flags}"}) side_effects -> !u32i + cir.store align(4) %2, %0 : !u32i, !cir.ptr + %3 = cir.load align(4) %0 : !cir.ptr, !u32i + cir.return %3 : !u32i +} +} From 2f6570bbad7a2352c84a0ee6a14074ad67c64d12 Mon Sep 17 00:00:00 2001 From: Iris Shi <0.0@owo.li> Date: Thu, 14 Aug 2025 11:17:54 +0800 Subject: [PATCH 03/12] Apply review suggestions Co-authored-by: Andy Kaylor --- clang/include/clang/CIR/Dialect/IR/CIROps.td | 12 ++--- clang/lib/CIR/Dialect/IR/CIRDialect.cpp | 50 +++++++++++--------- 2 files changed, 32 insertions(+), 30 deletions(-) diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td b/clang/include/clang/CIR/Dialect/IR/CIROps.td index 08afd2ddb1b80..a77e9199cdc96 100644 --- a/clang/include/clang/CIR/Dialect/IR/CIROps.td +++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td @@ -2266,8 +2266,8 @@ def CIR_InlineAsmOp : CIR_Op<"asm", [RecursiveMemoryEffects]> { let description = [{ The `cir.asm` operation represents C/C++ asm inline. - CIR constraints strings follow barely the same rules that are established - for the C level assembler constraints with several differences caused by + CIR constraints strings follow the same rules that are established for + the C level assembler constraints with several differences caused by clang::AsmStmt processing. Thus, numbers that appears in the constraint string may also refer to: @@ -2277,11 +2277,9 @@ def CIR_InlineAsmOp : CIR_Op<"asm", [RecursiveMemoryEffects]> { Operand attributes are a storage, where each element corresponds to the operand with the same index. The first index relates to the operation result (if any). - Note, the operands themselves are stored as VariadicOfVariadic in the next - order: output, input and then in/out operands. - - Note, when several output operands are present, the result type may be - represented as an anon record type. + The operands themselves are stored as VariadicOfVariadic in the following + order: output, input and then in/out operands. When several output operands + are present, the result type may be represented as an anonymous record type. Example: ```C++ diff --git a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp index 2404755cde1e0..55f3262a32abd 100644 --- a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp +++ b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp @@ -2432,7 +2432,7 @@ void cir::InlineAsmOp::print(OpAsmPrinter &p) { auto *nameIt = names.begin(); auto *attrIt = getOperandAttrs().begin(); - for (auto ops : getAsmOperands()) { + for (mlir::OperandRange ops : getAsmOperands()) { p << *nameIt << " = "; p << '['; @@ -2504,7 +2504,7 @@ ParseResult cir::InlineAsmOp::parse(OpAsmParser &parser, llvm::SmallVector operandsGroupSizes; std::string asmString, constraints; Type resType; - auto *ctxt = parser.getBuilder().getContext(); + MLIRContext *ctxt = parser.getBuilder().getContext(); auto error = [&](const Twine &msg) -> LogicalResult { return parser.emitError(parser.getCurrentLocation(), msg); @@ -2528,7 +2528,7 @@ ParseResult cir::InlineAsmOp::parse(OpAsmParser &parser, OpAsmParser::UnresolvedOperand op; if (parser.parseOperand(op) || parser.parseColon()) - return mlir::failure(); + return error("can't parse operand"); Type typ; if (parser.parseType(typ).failed()) @@ -2556,26 +2556,30 @@ ParseResult cir::InlineAsmOp::parse(OpAsmParser &parser, return mlir::success(); } - if (parser.parseCommaSeparatedList([&]() { - Value val; - if (parseValue(val).succeeded()) { - result.operands.push_back(val); - size++; - - if (parser.parseOptionalLParen().failed()) { - operandAttrs.push_back(mlir::Attribute()); - return mlir::success(); - } - - if (parser.parseKeyword("maybe_memory").succeeded()) { - operandAttrs.push_back(mlir::UnitAttr::get(ctxt)); - if (parser.parseRParen()) - return expected(")"); - return mlir::success(); - } - } - return mlir::failure(); - })) + auto parseOperand = [&]() { + Value val; + if (parseValue(val).succeeded()) { + result.operands.push_back(val); + size++; + + if (parser.parseOptionalLParen().failed()) { + operandAttrs.push_back(mlir::Attribute()); + return mlir::success(); + } + + if (parser.parseKeyword("maybe_memory").succeeded()) { + operandAttrs.push_back(mlir::UnitAttr::get(ctxt)); + if (parser.parseRParen()) + return expected(")"); + return mlir::success(); + } else { + return expected("maybe_memory"); + } + } + return mlir::failure(); + }; + + if (parser.parseCommaSeparatedList(parseOperand).failed()) return mlir::failure(); if (parser.parseRSquare().failed() || parser.parseComma().failed()) From 0c87d3bec68b06ffaeb05b68d3ebcaab1d3b8b41 Mon Sep 17 00:00:00 2001 From: Iris Shi <0.0@owo.li> Date: Thu, 14 Aug 2025 23:19:49 +0800 Subject: [PATCH 04/12] Update clang/lib/CIR/Dialect/IR/CIRDialect.cpp Co-authored-by: Morris Hafner --- clang/lib/CIR/Dialect/IR/CIRDialect.cpp | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp index 55f3262a32abd..7984f735d8e25 100644 --- a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp +++ b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp @@ -2459,13 +2459,10 @@ void cir::InlineAsmOp::print(OpAsmPrinter &p) { if (getSideEffects()) p << " side_effects"; - llvm::SmallVector<::llvm::StringRef, 2> elidedAttrs; - elidedAttrs.push_back("asm_flavor"); - elidedAttrs.push_back("asm_string"); - elidedAttrs.push_back("constraints"); - elidedAttrs.push_back("operand_attrs"); - elidedAttrs.push_back("operands_segments"); - elidedAttrs.push_back("side_effects"); + std::array elidedAttrs{ + llvm::StringRef("asm_flavor"), llvm::StringRef("asm_string"), + llvm::StringRef("constraints"), llvm::StringRef("operand_attrs"), + llvm::StringRef("operands_segments"), llvm::StringRef("side_effects")}; p.printOptionalAttrDict(getOperation()->getAttrs(), elidedAttrs); if (auto v = getRes()) From 963cf4441b361f9e4b6135b75d00db20e8299367 Mon Sep 17 00:00:00 2001 From: Iris Shi <0.0@owo.li> Date: Wed, 13 Aug 2025 18:49:31 +0800 Subject: [PATCH 05/12] [CIR] Add InlineAsmOp lowering to LLVM --- .../CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp | 67 +++++++++++++++ .../CIR/Lowering/DirectToLLVM/LowerToLLVM.h | 17 ++++ clang/test/CIR/Lowering/inline-asm.cir | 86 +++++++++++++++++++ 3 files changed, 170 insertions(+) create mode 100644 clang/test/CIR/Lowering/inline-asm.cir diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp index ad5f52034f92a..cd36b42ce3e28 100644 --- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp +++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp @@ -2272,6 +2272,8 @@ void ConvertCIRToLLVMPass::runOnOperation() { patterns.add(converter, patterns.getContext(), dl); patterns.add(converter, patterns.getContext(), dl); + patterns.add(converter, patterns.getContext(), + dl); patterns.add< // clang-format off CIRToLLVMAssumeOpLowering, @@ -2905,6 +2907,71 @@ mlir::LogicalResult CIRToLLVMGetBitfieldOpLowering::matchAndRewrite( return mlir::success(); } +mlir::LogicalResult CIRToLLVMInlineAsmOpLowering::matchAndRewrite( + cir::InlineAsmOp op, OpAdaptor adaptor, + mlir::ConversionPatternRewriter &rewriter) const { + mlir::Type llResTy; + if (op.getNumResults()) + llResTy = getTypeConverter()->convertType(op.getType(0)); + + auto dialect = op.getAsmFlavor(); + auto llDialect = dialect == cir::AsmFlavor::x86_att + ? mlir::LLVM::AsmDialect::AD_ATT + : mlir::LLVM::AsmDialect::AD_Intel; + + SmallVector opAttrs; + auto llvmAttrName = mlir::LLVM::InlineAsmOp::getElementTypeAttrName(); + + // this is for the lowering to LLVM from LLVM dialect. Otherwise, if we + // don't have the result (i.e. void type as a result of operation), the + // element type attribute will be attached to the whole instruction, but not + // to the operand + if (!op.getNumResults()) + opAttrs.push_back(mlir::Attribute()); + + SmallVector llvmOperands; + SmallVector cirOperands; + auto llvmAsmOps = adaptor.getAsmOperands(); + auto cirAsmOps = op.getAsmOperands(); + for (size_t i = 0; i < llvmAsmOps.size(); ++i) { + auto llvmOps = llvmAsmOps[i]; + auto cirOps = cirAsmOps[i]; + llvmOperands.append(llvmOps.begin(), llvmOps.end()); + cirOperands.append(cirOps.begin(), cirOps.end()); + } + + // so far we infer the llvm dialect element type attr from + // CIR operand type. + auto cirOpAttrs = op.getOperandAttrs(); + for (std::size_t i = 0; i < cirOpAttrs.size(); ++i) { + if (!cirOpAttrs[i]) { + opAttrs.push_back(mlir::Attribute()); + continue; + } + + SmallVector attrs; + auto typ = cast(cirOperands[i].getType()); + auto typAttr = mlir::TypeAttr::get(convertTypeForMemory( + *getTypeConverter(), dataLayout, typ.getPointee())); + + attrs.push_back(rewriter.getNamedAttr(llvmAttrName, typAttr)); + auto newDict = rewriter.getDictionaryAttr(attrs); + opAttrs.push_back(newDict); + } + + rewriter.replaceOpWithNewOp( + op, llResTy, llvmOperands, op.getAsmStringAttr(), op.getConstraintsAttr(), + op.getSideEffectsAttr(), + /*is_align_stack*/ mlir::UnitAttr(), + /*tail_call_kind*/ + mlir::LLVM::TailCallKindAttr::get( + getContext(), mlir::LLVM::tailcallkind::TailCallKind::None), + mlir::LLVM::AsmDialectAttr::get(getContext(), llDialect), + rewriter.getArrayAttr(opAttrs)); + + return mlir::success(); +} + std::unique_ptr createConvertCIRToLLVMPass() { return std::make_unique(); } diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.h b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.h index a6d2d6559005b..a77562f242432 100644 --- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.h +++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.h @@ -657,6 +657,23 @@ class CIRToLLVMFAbsOpLowering : public mlir::OpConversionPattern { mlir::ConversionPatternRewriter &) const override; }; +class CIRToLLVMInlineAsmOpLowering + : public mlir::OpConversionPattern { + mlir::DataLayout const &dataLayout; + +public: + CIRToLLVMInlineAsmOpLowering(const mlir::TypeConverter &typeConverter, + mlir::MLIRContext *context, + mlir::DataLayout const &dataLayout) + : OpConversionPattern(typeConverter, context), dataLayout(dataLayout) {} + + using mlir::OpConversionPattern::OpConversionPattern; + + mlir::LogicalResult + matchAndRewrite(cir::InlineAsmOp op, OpAdaptor, + mlir::ConversionPatternRewriter &) const override; +}; + } // namespace direct } // namespace cir diff --git a/clang/test/CIR/Lowering/inline-asm.cir b/clang/test/CIR/Lowering/inline-asm.cir new file mode 100644 index 0000000000000..a8545d4c0f059 --- /dev/null +++ b/clang/test/CIR/Lowering/inline-asm.cir @@ -0,0 +1,86 @@ +// RUN: cir-translate %s -cir-to-llvmir --target x86_64-unknown-linux-gnu --disable-cc-lowering | FileCheck %s + +!s32i = !cir.int +!u32i = !cir.int + +module { +cir.func @f1() { + // CHECK: call void asm "", "~{dirflag},~{fpsr},~{flags}"() + cir.asm(x86_att, + out = [], + in = [], + in_out = [], + {"" "~{dirflag},~{fpsr},~{flags}"}) + cir.return +} + +cir.func @f2() { + // CHECK: call void asm sideeffect "", "~{dirflag},~{fpsr},~{flags}"() + cir.asm(x86_att, + out = [], + in = [], + in_out = [], + {"" "~{dirflag},~{fpsr},~{flags}"}) side_effects + cir.return +} + +cir.func @f3() { + // CHECK: call void asm sideeffect "abc", "~{dirflag},~{fpsr},~{flags}"() + cir.asm(x86_att, + out = [], + in = [], + in_out = [], + {"abc" "~{dirflag},~{fpsr},~{flags}"}) side_effects + cir.return +} + +cir.func @f4(%arg0: !s32i) { + %0 = cir.alloca !s32i, !cir.ptr, ["x", init] {alignment = 4 : i64} + cir.store %arg0, %0 : !s32i, !cir.ptr + // CHECK: call void asm sideeffect "", "*m,~{dirflag},~{fpsr},~{flags}"(ptr elementtype(i32) %2) + cir.asm(x86_att, + out = [], + in = [%0 : !cir.ptr (maybe_memory)], + in_out = [], + {"" "*m,~{dirflag},~{fpsr},~{flags}"}) side_effects + cir.return +} + +cir.func @f5() { + // CHECK: call void asm inteldialect "", "~{dirflag},~{fpsr},~{flags}"() + cir.asm(x86_intel, + out = [], + in = [], + in_out = [], + {"" "~{dirflag},~{fpsr},~{flags}"}) + cir.return +} + +cir.func @f6() -> !s32i { + %0 = cir.alloca !s32i, !cir.ptr, ["x", init] {alignment = 4 : i64} + // CHECK: %2 = call i32 asm sideeffect "movl $$42, $0", "=r,~{dirflag},~{fpsr},~{flags}"() + %1 = cir.asm(x86_att, + out = [], + in = [], + in_out = [], + {"movl $$42, $0" "=r,~{dirflag},~{fpsr},~{flags}"}) side_effects -> !s32i + cir.store align(4) %1, %0 : !s32i, !cir.ptr + %3 = cir.load align(4) %0 : !cir.ptr, !s32i + cir.return %3 : !s32i +} + +cir.func @f7(%arg0: !u32i) -> !u32i { + %0 = cir.alloca !u32i, !cir.ptr, ["x", init] {alignment = 4 : i64} + cir.store %arg0, %0 : !u32i, !cir.ptr + %1 = cir.load align(4) %0 : !cir.ptr, !u32i + // CHECK: %4 = call i32 asm sideeffect "addl $$42, $0", "=r,0,~{dirflag},~{fpsr},~{flags}"(i32 %3) + %2 = cir.asm(x86_att, + out = [], + in = [], + in_out = [%1 : !u32i], + {"addl $$42, $0" "=r,0,~{dirflag},~{fpsr},~{flags}"}) side_effects -> !u32i + cir.store align(4) %2, %0 : !u32i, !cir.ptr + %3 = cir.load align(4) %0 : !cir.ptr, !u32i + cir.return %3 : !u32i +} +} From 0b5b910dbd5bcf549e9a9bed73198172906a8538 Mon Sep 17 00:00:00 2001 From: Iris Shi <0.0@owo.li> Date: Thu, 14 Aug 2025 11:35:17 +0800 Subject: [PATCH 06/12] Apply review suggestions --- .../CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp | 33 +++++++++---------- 1 file changed, 15 insertions(+), 18 deletions(-) diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp index cd36b42ce3e28..172cde0301e1b 100644 --- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp +++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp @@ -2914,13 +2914,13 @@ mlir::LogicalResult CIRToLLVMInlineAsmOpLowering::matchAndRewrite( if (op.getNumResults()) llResTy = getTypeConverter()->convertType(op.getType(0)); - auto dialect = op.getAsmFlavor(); - auto llDialect = dialect == cir::AsmFlavor::x86_att - ? mlir::LLVM::AsmDialect::AD_ATT - : mlir::LLVM::AsmDialect::AD_Intel; + cir::AsmFlavor dialect = op.getAsmFlavor(); + mlir::LLVM::AsmDialect llDialect = dialect == cir::AsmFlavor::x86_att + ? mlir::LLVM::AsmDialect::AD_ATT + : mlir::LLVM::AsmDialect::AD_Intel; SmallVector opAttrs; - auto llvmAttrName = mlir::LLVM::InlineAsmOp::getElementTypeAttrName(); + StringRef llvmAttrName = mlir::LLVM::InlineAsmOp::getElementTypeAttrName(); // this is for the lowering to LLVM from LLVM dialect. Otherwise, if we // don't have the result (i.e. void type as a result of operation), the @@ -2931,31 +2931,28 @@ mlir::LogicalResult CIRToLLVMInlineAsmOpLowering::matchAndRewrite( SmallVector llvmOperands; SmallVector cirOperands; - auto llvmAsmOps = adaptor.getAsmOperands(); - auto cirAsmOps = op.getAsmOperands(); - for (size_t i = 0; i < llvmAsmOps.size(); ++i) { - auto llvmOps = llvmAsmOps[i]; - auto cirOps = cirAsmOps[i]; - llvmOperands.append(llvmOps.begin(), llvmOps.end()); - cirOperands.append(cirOps.begin(), cirOps.end()); + for (auto [llvmOp, cirOp] : + llvm::zip(adaptor.getAsmOperands(), op.getAsmOperands())) { + llvmOperands.append(llvmOp.begin(), llvmOp.end()); + cirOperands.append(cirOp.begin(), cirOp.end()); } // so far we infer the llvm dialect element type attr from // CIR operand type. - auto cirOpAttrs = op.getOperandAttrs(); - for (std::size_t i = 0; i < cirOpAttrs.size(); ++i) { - if (!cirOpAttrs[i]) { + for (auto [i, cirOpAttr] : llvm::enumerate(op.getOperandAttrs())) { + if (!cirOpAttr) { opAttrs.push_back(mlir::Attribute()); continue; } SmallVector attrs; - auto typ = cast(cirOperands[i].getType()); - auto typAttr = mlir::TypeAttr::get(convertTypeForMemory( + cir::PointerType typ = + mlir::cast(cirOperands[i].getType()); + mlir::TypeAttr typAttr = mlir::TypeAttr::get(convertTypeForMemory( *getTypeConverter(), dataLayout, typ.getPointee())); attrs.push_back(rewriter.getNamedAttr(llvmAttrName, typAttr)); - auto newDict = rewriter.getDictionaryAttr(attrs); + mlir::DictionaryAttr newDict = rewriter.getDictionaryAttr(attrs); opAttrs.push_back(newDict); } From dec7dd8c9da35b30655d564978e6e623474a6540 Mon Sep 17 00:00:00 2001 From: Iris Shi <0.0@owo.li> Date: Thu, 14 Aug 2025 23:57:19 +0800 Subject: [PATCH 07/12] Address comments Co-authored-by: Morris Hafner --- .../lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp index 172cde0301e1b..2546558bc66ce 100644 --- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp +++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp @@ -2931,23 +2931,23 @@ mlir::LogicalResult CIRToLLVMInlineAsmOpLowering::matchAndRewrite( SmallVector llvmOperands; SmallVector cirOperands; - for (auto [llvmOp, cirOp] : - llvm::zip(adaptor.getAsmOperands(), op.getAsmOperands())) { - llvmOperands.append(llvmOp.begin(), llvmOp.end()); - cirOperands.append(cirOp.begin(), cirOp.end()); + for (auto const&[llvmOp, cirOp] : + zip(adaptor.getAsmOperands(), op.getAsmOperands())) { + append_range(llvmOperands, llvmOp); + append_range(cirOperands, cirOp); } // so far we infer the llvm dialect element type attr from // CIR operand type. - for (auto [i, cirOpAttr] : llvm::enumerate(op.getOperandAttrs())) { + for (auto const&[cirOpAttr, cirOp] : zip(op.getOperandAttrs(), cirOperands)) { if (!cirOpAttr) { opAttrs.push_back(mlir::Attribute()); continue; } - SmallVector attrs; + llvm::SmallVector attrs; cir::PointerType typ = - mlir::cast(cirOperands[i].getType()); + mlir::cast(cirOp.getType()); mlir::TypeAttr typAttr = mlir::TypeAttr::get(convertTypeForMemory( *getTypeConverter(), dataLayout, typ.getPointee())); From 606173188bb9c50ab3ca5f3b8aa36f4b06a2e635 Mon Sep 17 00:00:00 2001 From: Iris Shi <0.0@owo.li> Date: Wed, 13 Aug 2025 13:18:57 +0800 Subject: [PATCH 08/12] [CIR] Add `InlineAsmOp` --- clang/include/clang/CIR/Dialect/IR/CIROps.td | 83 ++++++++ clang/lib/CIR/Dialect/IR/CIRDialect.cpp | 202 +++++++++++++++++++ 2 files changed, 285 insertions(+) diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td b/clang/include/clang/CIR/Dialect/IR/CIROps.td index b64fd2734a63c..08afd2ddb1b80 100644 --- a/clang/include/clang/CIR/Dialect/IR/CIROps.td +++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td @@ -2254,6 +2254,89 @@ def CIR_StackRestoreOp : CIR_Op<"stackrestore"> { let assemblyFormat = "$ptr attr-dict `:` qualified(type($ptr))"; } +//===----------------------------------------------------------------------===// +// InlineAsmOp +//===----------------------------------------------------------------------===// + +def CIR_AsmFlavor : CIR_I32EnumAttr<"AsmFlavor", "ATT or Intel", + [I32EnumAttrCase<"x86_att", 0>, + I32EnumAttrCase<"x86_intel", 1>]>; + +def CIR_InlineAsmOp : CIR_Op<"asm", [RecursiveMemoryEffects]> { + let description = [{ + The `cir.asm` operation represents C/C++ asm inline. + + CIR constraints strings follow barely the same rules that are established + for the C level assembler constraints with several differences caused by + clang::AsmStmt processing. + + Thus, numbers that appears in the constraint string may also refer to: + - the output variable index referenced by the input operands. + - the index of early-clobber operand + + Operand attributes are a storage, where each element corresponds to the + operand with the same index. The first index relates to the operation + result (if any). + Note, the operands themselves are stored as VariadicOfVariadic in the next + order: output, input and then in/out operands. + + Note, when several output operands are present, the result type may be + represented as an anon record type. + + Example: + ```C++ + __asm__("foo" : : : ); + __asm__("bar $42 %[val]" : [val] "=r" (x), "+&r"(x)); + __asm__("baz $42 %[val]" : [val] "=r" (x), "+&r"(x) : "[val]"(y)); + ``` + + ```mlir + !rec_22anon2E022 = !cir.record, !cir.int}> + !rec_22anon2E122 = !cir.record, !cir.int}> + ... + %0 = cir.alloca !s32i, !cir.ptr, ["x", init] + %1 = cir.alloca !s32i, !cir.ptr, ["y", init] + ... + %2 = cir.load %0 : !cir.ptr, !s32i + %3 = cir.load %1 : !cir.ptr, !s32i + + cir.asm(x86_att, + out = [], + in = [], + in_out = [], + {"foo" "~{dirflag},~{fpsr},~{flags}"}) side_effects + + cir.asm(x86_att, + out = [], + in = [], + in_out = [%2 : !s32i], + {"bar $$42 $0" "=r,=&r,1,~{dirflag},~{fpsr},~{flags}"}) -> !rec_22anon2E022 + + cir.asm(x86_att, + out = [], + in = [%3 : !s32i], + in_out = [%2 : !s32i], + {"baz $$42 $0" "=r,=&r,0,1,~{dirflag},~{fpsr},~{flags}"}) -> !rec_22anon2E122 + ``` + }]; + + let results = (outs Optional:$res); + + let arguments = + (ins VariadicOfVariadic:$asm_operands, + StrAttr:$asm_string, StrAttr:$constraints, UnitAttr:$side_effects, + CIR_AsmFlavor:$asm_flavor, ArrayAttr:$operand_attrs, + DenseI32ArrayAttr:$operands_segments); + + let builders = [OpBuilder<(ins + "llvm::ArrayRef":$asmOperands, + "llvm::StringRef":$asmString, "llvm::StringRef":$constraints, + "bool":$sideEffects, "AsmFlavor":$asmFlavor, + "llvm::ArrayRef":$operandAttrs)>]; + + let hasCustomAssemblyFormat = 1; +} + //===----------------------------------------------------------------------===// // UnreachableOp //===----------------------------------------------------------------------===// diff --git a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp index 936247e9d8fbb..2404755cde1e0 100644 --- a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp +++ b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp @@ -2419,6 +2419,208 @@ OpFoldResult RotateOp::fold(FoldAdaptor adaptor) { return IntAttr::get(input.getContext(), input.getType(), resultValue); } +//===----------------------------------------------------------------------===// +// InlineAsmOp +//===----------------------------------------------------------------------===// + +void cir::InlineAsmOp::print(OpAsmPrinter &p) { + p << '(' << getAsmFlavor() << ", "; + p.increaseIndent(); + p.printNewline(); + + llvm::SmallVector names{"out", "in", "in_out"}; + auto *nameIt = names.begin(); + auto *attrIt = getOperandAttrs().begin(); + + for (auto ops : getAsmOperands()) { + p << *nameIt << " = "; + + p << '['; + llvm::interleaveComma(llvm::make_range(ops.begin(), ops.end()), p, + [&](Value value) { + p.printOperand(value); + p << " : " << value.getType(); + if (*attrIt) + p << " (maybe_memory)"; + attrIt++; + }); + p << "],"; + p.printNewline(); + ++nameIt; + } + + p << "{"; + p.printString(getAsmString()); + p << " "; + p.printString(getConstraints()); + p << "}"; + p.decreaseIndent(); + p << ')'; + if (getSideEffects()) + p << " side_effects"; + + llvm::SmallVector<::llvm::StringRef, 2> elidedAttrs; + elidedAttrs.push_back("asm_flavor"); + elidedAttrs.push_back("asm_string"); + elidedAttrs.push_back("constraints"); + elidedAttrs.push_back("operand_attrs"); + elidedAttrs.push_back("operands_segments"); + elidedAttrs.push_back("side_effects"); + p.printOptionalAttrDict(getOperation()->getAttrs(), elidedAttrs); + + if (auto v = getRes()) + p << " -> " << v.getType(); +} + +void cir::InlineAsmOp::build(OpBuilder &odsBuilder, OperationState &odsState, + ArrayRef asmOperands, + StringRef asmString, StringRef constraints, + bool sideEffects, cir::AsmFlavor asmFlavor, + ArrayRef operandAttrs) { + // Set up the operands_segments for VariadicOfVariadic + SmallVector segments; + for (auto operandRange : asmOperands) { + segments.push_back(operandRange.size()); + odsState.addOperands(operandRange); + } + + odsState.addAttribute( + "operands_segments", + DenseI32ArrayAttr::get(odsBuilder.getContext(), segments)); + odsState.addAttribute("asm_string", odsBuilder.getStringAttr(asmString)); + odsState.addAttribute("constraints", odsBuilder.getStringAttr(constraints)); + odsState.addAttribute("asm_flavor", + AsmFlavorAttr::get(odsBuilder.getContext(), asmFlavor)); + + if (sideEffects) + odsState.addAttribute("side_effects", odsBuilder.getUnitAttr()); + + odsState.addAttribute("operand_attrs", odsBuilder.getArrayAttr(operandAttrs)); +} + +ParseResult cir::InlineAsmOp::parse(OpAsmParser &parser, + OperationState &result) { + llvm::SmallVector operandAttrs; + llvm::SmallVector operandsGroupSizes; + std::string asmString, constraints; + Type resType; + auto *ctxt = parser.getBuilder().getContext(); + + auto error = [&](const Twine &msg) -> LogicalResult { + return parser.emitError(parser.getCurrentLocation(), msg); + }; + + auto expected = [&](const std::string &c) { + return error("expected '" + c + "'"); + }; + + if (parser.parseLParen().failed()) + return expected("("); + + auto flavor = FieldParser::parse(parser); + if (failed(flavor)) + return error("Unknown AsmFlavor"); + + if (parser.parseComma().failed()) + return expected(","); + + auto parseValue = [&](Value &v) { + OpAsmParser::UnresolvedOperand op; + + if (parser.parseOperand(op) || parser.parseColon()) + return mlir::failure(); + + Type typ; + if (parser.parseType(typ).failed()) + return error("can't parse operand type"); + llvm::SmallVector tmp; + if (parser.resolveOperand(op, typ, tmp)) + return error("can't resolve operand"); + v = tmp[0]; + return mlir::success(); + }; + + auto parseOperands = [&](llvm::StringRef name) { + if (parser.parseKeyword(name).failed()) + return error("expected " + name + " operands here"); + if (parser.parseEqual().failed()) + return expected("="); + if (parser.parseLSquare().failed()) + return expected("["); + + int size = 0; + if (parser.parseOptionalRSquare().succeeded()) { + operandsGroupSizes.push_back(size); + if (parser.parseComma()) + return expected(","); + return mlir::success(); + } + + if (parser.parseCommaSeparatedList([&]() { + Value val; + if (parseValue(val).succeeded()) { + result.operands.push_back(val); + size++; + + if (parser.parseOptionalLParen().failed()) { + operandAttrs.push_back(mlir::Attribute()); + return mlir::success(); + } + + if (parser.parseKeyword("maybe_memory").succeeded()) { + operandAttrs.push_back(mlir::UnitAttr::get(ctxt)); + if (parser.parseRParen()) + return expected(")"); + return mlir::success(); + } + } + return mlir::failure(); + })) + return mlir::failure(); + + if (parser.parseRSquare().failed() || parser.parseComma().failed()) + return expected("]"); + operandsGroupSizes.push_back(size); + return mlir::success(); + }; + + if (parseOperands("out").failed() || parseOperands("in").failed() || + parseOperands("in_out").failed()) + return error("failed to parse operands"); + + if (parser.parseLBrace()) + return expected("{"); + if (parser.parseString(&asmString)) + return error("asm string parsing failed"); + if (parser.parseString(&constraints)) + return error("constraints string parsing failed"); + if (parser.parseRBrace()) + return expected("}"); + if (parser.parseRParen()) + return expected(")"); + + if (parser.parseOptionalKeyword("side_effects").succeeded()) + result.attributes.set("side_effects", UnitAttr::get(ctxt)); + + if (parser.parseOptionalArrow().succeeded() && + parser.parseType(resType).failed()) + return mlir::failure(); + + if (parser.parseOptionalAttrDict(result.attributes)) + return mlir::failure(); + + result.attributes.set("asm_flavor", AsmFlavorAttr::get(ctxt, *flavor)); + result.attributes.set("asm_string", StringAttr::get(ctxt, asmString)); + result.attributes.set("constraints", StringAttr::get(ctxt, constraints)); + result.attributes.set("operand_attrs", ArrayAttr::get(ctxt, operandAttrs)); + result.getOrAddProperties().operands_segments = + parser.getBuilder().getDenseI32ArrayAttr(operandsGroupSizes); + if (resType) + result.addTypes(TypeRange{resType}); + + return mlir::success(); +} + //===----------------------------------------------------------------------===// // TableGen'd op method definitions //===----------------------------------------------------------------------===// From 8e86b93f1e348169aa3c2eba75903bf33f154f97 Mon Sep 17 00:00:00 2001 From: Iris Shi <0.0@owo.li> Date: Wed, 13 Aug 2025 15:51:03 +0800 Subject: [PATCH 09/12] add parsing test --- clang/test/CIR/IR/inline-asm.cir | 112 +++++++++++++++++++++++++++++++ 1 file changed, 112 insertions(+) create mode 100644 clang/test/CIR/IR/inline-asm.cir diff --git a/clang/test/CIR/IR/inline-asm.cir b/clang/test/CIR/IR/inline-asm.cir new file mode 100644 index 0000000000000..fb1f6315a7d72 --- /dev/null +++ b/clang/test/CIR/IR/inline-asm.cir @@ -0,0 +1,112 @@ +// RUN: cir-opt %s | FileCheck %s + +!s32i = !cir.int +!u32i = !cir.int + +module { +cir.func @f1() { + // CHECK: cir.asm(x86_att, + // CHECK: out = [], + // CHECK: in = [], + // CHECK: in_out = [], + // CHECK: {"" "~{dirflag},~{fpsr},~{flags}"}) + cir.asm(x86_att, + out = [], + in = [], + in_out = [], + {"" "~{dirflag},~{fpsr},~{flags}"}) + cir.return +} + +cir.func @f2() { + // CHECK: cir.asm(x86_att, + // CHECK: out = [], + // CHECK: in = [], + // CHECK: in_out = [], + // CHECK: {"" "~{dirflag},~{fpsr},~{flags}"}) side_effects + cir.asm(x86_att, + out = [], + in = [], + in_out = [], + {"" "~{dirflag},~{fpsr},~{flags}"}) side_effects + cir.return +} + +cir.func @f3() { + // CHECK: cir.asm(x86_att, + // CHECK: out = [], + // CHECK: in = [], + // CHECK: in_out = [], + // CHECK: {"abc" "~{dirflag},~{fpsr},~{flags}"}) side_effects + cir.asm(x86_att, + out = [], + in = [], + in_out = [], + {"abc" "~{dirflag},~{fpsr},~{flags}"}) side_effects + cir.return +} + +cir.func @f4(%arg0: !s32i) { + %0 = cir.alloca !s32i, !cir.ptr, ["x", init] {alignment = 4 : i64} + cir.store %arg0, %0 : !s32i, !cir.ptr + // CHECK: cir.asm(x86_att, + // CHECK: out = [], + // CHECK: in = [%0 : !cir.ptr (maybe_memory)], + // CHECK: in_out = [], + // CHECK: {"" "*m,~{dirflag},~{fpsr},~{flags}"}) side_effects + cir.asm(x86_att, + out = [], + in = [%0 : !cir.ptr (maybe_memory)], + in_out = [], + {"" "*m,~{dirflag},~{fpsr},~{flags}"}) side_effects + cir.return +} + +cir.func @f5() { + // CHECK: cir.asm(x86_intel, + // CHECK: out = [], + // CHECK: in = [], + // CHECK: in_out = [], + // CHECK: {"" "~{dirflag},~{fpsr},~{flags}"}) + cir.asm(x86_intel, + out = [], + in = [], + in_out = [], + {"" "~{dirflag},~{fpsr},~{flags}"}) + cir.return +} +cir.func @f6() -> !s32i { + %0 = cir.alloca !s32i, !cir.ptr, ["x", init] {alignment = 4 : i64} + // CHECK: %1 = cir.asm(x86_att, + // CHECK: out = [], + // CHECK: in = [], + // CHECK: in_out = [], + // CHECK: {"movl $$42, $0" "=r,~{dirflag},~{fpsr},~{flags}"}) side_effects -> !s32i + %1 = cir.asm(x86_att, + out = [], + in = [], + in_out = [], + {"movl $$42, $0" "=r,~{dirflag},~{fpsr},~{flags}"}) side_effects -> !s32i + cir.store align(4) %1, %0 : !s32i, !cir.ptr + %3 = cir.load align(4) %0 : !cir.ptr, !s32i + cir.return %3 : !s32i +} +cir.func @f7(%arg0: !u32i) -> !u32i { + %0 = cir.alloca !u32i, !cir.ptr, ["x", init] {alignment = 4 : i64} + cir.store %arg0, %0 : !u32i, !cir.ptr + %1 = cir.load align(4) %0 : !cir.ptr, !u32i + // CHECK: %2 = cir.asm(x86_att, + // CHECK: out = [], + // CHECK: in = [], + // CHECK: in_out = [%1 : !u32i], + // CHECK: {"addl $$42, $0" "=r,0,~{dirflag},~{fpsr},~{flags}"}) side_effects -> !u32i + %2 = cir.asm(x86_att, + out = [], + in = [], + in_out = [%1 : !u32i], + {"addl $$42, $0" "=r,0,~{dirflag},~{fpsr},~{flags}"}) side_effects -> !u32i + cir.store align(4) %2, %0 : !u32i, !cir.ptr + %3 = cir.load align(4) %0 : !cir.ptr, !u32i + cir.return %3 : !u32i +} +} From 0282e282f209d6ef782872efabe916c05f1e61c1 Mon Sep 17 00:00:00 2001 From: Iris Shi <0.0@owo.li> Date: Thu, 14 Aug 2025 11:17:54 +0800 Subject: [PATCH 10/12] Apply review suggestions Co-authored-by: Andy Kaylor --- clang/include/clang/CIR/Dialect/IR/CIROps.td | 12 ++--- clang/lib/CIR/Dialect/IR/CIRDialect.cpp | 50 +++++++++++--------- 2 files changed, 32 insertions(+), 30 deletions(-) diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td b/clang/include/clang/CIR/Dialect/IR/CIROps.td index 08afd2ddb1b80..a77e9199cdc96 100644 --- a/clang/include/clang/CIR/Dialect/IR/CIROps.td +++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td @@ -2266,8 +2266,8 @@ def CIR_InlineAsmOp : CIR_Op<"asm", [RecursiveMemoryEffects]> { let description = [{ The `cir.asm` operation represents C/C++ asm inline. - CIR constraints strings follow barely the same rules that are established - for the C level assembler constraints with several differences caused by + CIR constraints strings follow the same rules that are established for + the C level assembler constraints with several differences caused by clang::AsmStmt processing. Thus, numbers that appears in the constraint string may also refer to: @@ -2277,11 +2277,9 @@ def CIR_InlineAsmOp : CIR_Op<"asm", [RecursiveMemoryEffects]> { Operand attributes are a storage, where each element corresponds to the operand with the same index. The first index relates to the operation result (if any). - Note, the operands themselves are stored as VariadicOfVariadic in the next - order: output, input and then in/out operands. - - Note, when several output operands are present, the result type may be - represented as an anon record type. + The operands themselves are stored as VariadicOfVariadic in the following + order: output, input and then in/out operands. When several output operands + are present, the result type may be represented as an anonymous record type. Example: ```C++ diff --git a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp index 2404755cde1e0..55f3262a32abd 100644 --- a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp +++ b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp @@ -2432,7 +2432,7 @@ void cir::InlineAsmOp::print(OpAsmPrinter &p) { auto *nameIt = names.begin(); auto *attrIt = getOperandAttrs().begin(); - for (auto ops : getAsmOperands()) { + for (mlir::OperandRange ops : getAsmOperands()) { p << *nameIt << " = "; p << '['; @@ -2504,7 +2504,7 @@ ParseResult cir::InlineAsmOp::parse(OpAsmParser &parser, llvm::SmallVector operandsGroupSizes; std::string asmString, constraints; Type resType; - auto *ctxt = parser.getBuilder().getContext(); + MLIRContext *ctxt = parser.getBuilder().getContext(); auto error = [&](const Twine &msg) -> LogicalResult { return parser.emitError(parser.getCurrentLocation(), msg); @@ -2528,7 +2528,7 @@ ParseResult cir::InlineAsmOp::parse(OpAsmParser &parser, OpAsmParser::UnresolvedOperand op; if (parser.parseOperand(op) || parser.parseColon()) - return mlir::failure(); + return error("can't parse operand"); Type typ; if (parser.parseType(typ).failed()) @@ -2556,26 +2556,30 @@ ParseResult cir::InlineAsmOp::parse(OpAsmParser &parser, return mlir::success(); } - if (parser.parseCommaSeparatedList([&]() { - Value val; - if (parseValue(val).succeeded()) { - result.operands.push_back(val); - size++; - - if (parser.parseOptionalLParen().failed()) { - operandAttrs.push_back(mlir::Attribute()); - return mlir::success(); - } - - if (parser.parseKeyword("maybe_memory").succeeded()) { - operandAttrs.push_back(mlir::UnitAttr::get(ctxt)); - if (parser.parseRParen()) - return expected(")"); - return mlir::success(); - } - } - return mlir::failure(); - })) + auto parseOperand = [&]() { + Value val; + if (parseValue(val).succeeded()) { + result.operands.push_back(val); + size++; + + if (parser.parseOptionalLParen().failed()) { + operandAttrs.push_back(mlir::Attribute()); + return mlir::success(); + } + + if (parser.parseKeyword("maybe_memory").succeeded()) { + operandAttrs.push_back(mlir::UnitAttr::get(ctxt)); + if (parser.parseRParen()) + return expected(")"); + return mlir::success(); + } else { + return expected("maybe_memory"); + } + } + return mlir::failure(); + }; + + if (parser.parseCommaSeparatedList(parseOperand).failed()) return mlir::failure(); if (parser.parseRSquare().failed() || parser.parseComma().failed()) From 024a77e05434291d424ea7a8566d07fea96fc8f7 Mon Sep 17 00:00:00 2001 From: Iris Shi <0.0@owo.li> Date: Thu, 14 Aug 2025 23:19:49 +0800 Subject: [PATCH 11/12] Update clang/lib/CIR/Dialect/IR/CIRDialect.cpp Co-authored-by: Morris Hafner --- clang/lib/CIR/Dialect/IR/CIRDialect.cpp | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp index 55f3262a32abd..7984f735d8e25 100644 --- a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp +++ b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp @@ -2459,13 +2459,10 @@ void cir::InlineAsmOp::print(OpAsmPrinter &p) { if (getSideEffects()) p << " side_effects"; - llvm::SmallVector<::llvm::StringRef, 2> elidedAttrs; - elidedAttrs.push_back("asm_flavor"); - elidedAttrs.push_back("asm_string"); - elidedAttrs.push_back("constraints"); - elidedAttrs.push_back("operand_attrs"); - elidedAttrs.push_back("operands_segments"); - elidedAttrs.push_back("side_effects"); + std::array elidedAttrs{ + llvm::StringRef("asm_flavor"), llvm::StringRef("asm_string"), + llvm::StringRef("constraints"), llvm::StringRef("operand_attrs"), + llvm::StringRef("operands_segments"), llvm::StringRef("side_effects")}; p.printOptionalAttrDict(getOperation()->getAttrs(), elidedAttrs); if (auto v = getRes()) From abf9f5e5832f22be028bb55e6da691c5b2a2a6d2 Mon Sep 17 00:00:00 2001 From: Iris Shi <0.0@owo.li> Date: Fri, 15 Aug 2025 01:10:09 +0800 Subject: [PATCH 12/12] Update clang/lib/CIR/Dialect/IR/CIRDialect.cpp Co-authored-by: Andy Kaylor --- clang/lib/CIR/Dialect/IR/CIRDialect.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp index 7984f735d8e25..50246007b1072 100644 --- a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp +++ b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp @@ -2607,7 +2607,7 @@ ParseResult cir::InlineAsmOp::parse(OpAsmParser &parser, parser.parseType(resType).failed()) return mlir::failure(); - if (parser.parseOptionalAttrDict(result.attributes)) + if (parser.parseOptionalAttrDict(result.attributes).failed()) return mlir::failure(); result.attributes.set("asm_flavor", AsmFlavorAttr::get(ctxt, *flavor));