Skip to content

Commit

Permalink
[CIR][CIRGen] Inline asm: operand attributes (llvm#491)
Browse files Browse the repository at this point in the history
This is the next step in inline assembly support and it's more like a
service PR and mostly dedicated to the in/out argument types.

Also, operand attributes are added and it's the last change in the
`cir.asm` operation afaik. But I would wait untill the next PR,
which will contain more examples and maybe will help us to get more
readable format for the operation.
Note, that we have to add an attribute for each operand - because the
lowering of the llvm dialect to LLVM IR iterates over them in the same
order.

The next PR will be last one (so far) in the series of PRs dedicated to
the inline assembly support. It will add storing of the results.
  • Loading branch information
gitoleg authored and lanza committed Mar 20, 2024
1 parent 69ad7f8 commit 44b5456
Show file tree
Hide file tree
Showing 8 changed files with 185 additions and 31 deletions.
11 changes: 9 additions & 2 deletions clang/include/clang/CIR/Dialect/IR/CIROps.td
Original file line number Diff line number Diff line change
Expand Up @@ -3093,6 +3093,10 @@ def CIR_InlineAsmOp : CIR_Op<"asm", [RecursiveMemoryEffects]> {
- the output variable index referenced by the input operands.
- the index of early-clobber operand

Operand attributes is a storage of attributes, where each element corresponds
to the operand with the same index. The first index relates to the operation
result.

Example:
```C++
__asm__("foo" : : : );
Expand All @@ -3119,17 +3123,20 @@ def CIR_InlineAsmOp : CIR_Op<"asm", [RecursiveMemoryEffects]> {
StrAttr:$asm_string,
StrAttr:$constraints,
UnitAttr:$side_effects,
AsmFlavor:$asm_flavor);
AsmFlavor:$asm_flavor,
OptionalAttr<ArrayAttr>:$operand_attrs);

let assemblyFormat = [{
`(`
$asm_flavor`,`
`{` $asm_string $constraints `}`
`)`
(`operand_attrs` `=` $operand_attrs^)?
(`side_effects` $side_effects^)?
attr-dict
operands `:` functional-type(operands, results)
}];
}];

}

//===----------------------------------------------------------------------===//
Expand Down
5 changes: 5 additions & 0 deletions clang/lib/CIR/CodeGen/Address.h
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,11 @@ class Address {
return Alignment;
}

/// Return the type of the pointer value.
mlir::cir::PointerType getType() const {
return getPointer().getType().cast<mlir::cir::PointerType>();
}

mlir::Type getElementType() const {
assert(isValid());
return ElementType;
Expand Down
144 changes: 129 additions & 15 deletions clang/lib/CIR/CodeGen/CIRAsm.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,16 @@

#include "CIRGenFunction.h"
#include "TargetInfo.h"
#include "UnimplementedFeatureGuarding.h"

using namespace cir;
using namespace clang;
using namespace mlir::cir;

static bool isAggregateType(mlir::Type typ) {
return isa<mlir::cir::StructType, mlir::cir::ArrayType>(typ);
}

static AsmFlavor inferFlavor(const CIRGenModule &cgm, const AsmStmt &S) {
AsmFlavor GnuAsmFlavor =
cgm.getCodeGenOpts().getInlineAsmDialect() == CodeGenOptions::IAD_ATT
Expand Down Expand Up @@ -195,31 +200,32 @@ static void collectInOutConstrainsInfos(const CIRGenFunction &cgf,
}
}

mlir::Value CIRGenFunction::buildAsmInputLValue(
std::pair<mlir::Value, mlir::Type> CIRGenFunction::buildAsmInputLValue(
const TargetInfo::ConstraintInfo &Info, LValue InputValue,
QualType InputType, std::string &ConstraintStr, SourceLocation Loc) {

if (Info.allowsRegister() || !Info.allowsMemory()) {
if (hasScalarEvaluationKind(InputType))
return buildLoadOfLValue(InputValue, Loc).getScalarVal();
return {buildLoadOfLValue(InputValue, Loc).getScalarVal(), mlir::Type()};

mlir::Type Ty = convertType(InputType);
uint64_t Size = CGM.getDataLayout().getTypeSizeInBits(Ty);
if ((Size <= 64 && llvm::isPowerOf2_64(Size)) ||
getTargetHooks().isScalarizableAsmOperand(*this, Ty)) {
Ty = mlir::cir::IntType::get(builder.getContext(), Size, false);

return builder.createLoad(getLoc(Loc),
InputValue.getAddress().withElementType(Ty));
return {builder.createLoad(getLoc(Loc),
InputValue.getAddress().withElementType(Ty)),
mlir::Type()};
}
}

Address Addr = InputValue.getAddress();
ConstraintStr += '*';
return Addr.getPointer();
return {Addr.getPointer(), Addr.getElementType()};
}

mlir::Value
std::pair<mlir::Value, mlir::Type>
CIRGenFunction::buildAsmInput(const TargetInfo::ConstraintInfo &Info,
const Expr *InputExpr,
std::string &ConstraintStr) {
Expand All @@ -235,19 +241,19 @@ CIRGenFunction::buildAsmInput(const TargetInfo::ConstraintInfo &Info,
llvm::APSInt IntResult;
if (EVResult.Val.toIntegralConstant(IntResult, InputExpr->getType(),
getContext()))
return builder.getConstAPSInt(loc, IntResult);
return {builder.getConstAPSInt(loc, IntResult), mlir::Type()};
}

Expr::EvalResult Result;
if (InputExpr->EvaluateAsInt(Result, getContext()))
builder.getConstAPSInt(loc, Result.Val.getInt());
return {builder.getConstAPSInt(loc, Result.Val.getInt()), mlir::Type()};
}

if (Info.allowsRegister() || !Info.allowsMemory())
if (CIRGenFunction::hasScalarEvaluationKind(InputExpr->getType()))
return buildScalarExpr(InputExpr);
return {buildScalarExpr(InputExpr), nullptr};
if (InputExpr->getStmtClass() == Expr::CXXThisExprClass)
return buildScalarExpr(InputExpr);
return {buildScalarExpr(InputExpr), nullptr};
InputExpr = InputExpr->IgnoreParenNoopCasts(getContext());
LValue Dest = buildLValue(InputExpr);
return buildAsmInputLValue(Info, Dest, InputExpr->getType(), ConstraintStr,
Expand All @@ -265,12 +271,21 @@ mlir::LogicalResult CIRGenFunction::buildAsmStmt(const AsmStmt &S) {
InputConstraintInfos);

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;
llvm::BitVector ResultTypeRequiresCast;
llvm::BitVector ResultRegIsFlagReg;

// Keep track of input constraints.
std::string InOutConstraints;
std::vector<mlir::Value> InOutArgs;
std::vector<mlir::Type> InOutArgTypes;
std::vector<mlir::Type> InOutArgElemTypes;

// Keep track of out constraints for tied input operand.
std::vector<std::string> OutputConstraints;
Expand Down Expand Up @@ -312,6 +327,58 @@ mlir::LogicalResult CIRGenFunction::buildAsmStmt(const AsmStmt &S) {
hasScalarEvaluationKind(QTy) || hasAggregateEvaluationKind(QTy);
if (!Info.allowsMemory() && IsScalarOrAggregate) {
Constraints += "=" + OutputConstraint;
ResultRegQualTys.push_back(QTy);
ResultRegDests.push_back(Dest);

bool IsFlagReg = llvm::StringRef(OutputConstraint).startswith("{@cc");
ResultRegIsFlagReg.push_back(IsFlagReg);

mlir::Type Ty = convertTypeForMem(QTy);
const bool RequiresCast =
Info.allowsRegister() &&
(getTargetHooks().isScalarizableAsmOperand(*this, Ty) ||
isAggregateType(Ty));

ResultTruncRegTypes.push_back(Ty);
ResultTypeRequiresCast.push_back(RequiresCast);

if (RequiresCast) {
unsigned Size = getContext().getTypeSize(QTy);
Ty = mlir::cir::IntType::get(builder.getContext(), Size, false);
}
ResultRegTypes.push_back(Ty);
// If this output is tied to an input, and if the input is larger, then
// we need to set the actual result type of the inline asm node to be the
// same as the input type.
if (Info.hasMatchingInput()) {
unsigned InputNo;
for (InputNo = 0; InputNo != S.getNumInputs(); ++InputNo) {
TargetInfo::ConstraintInfo &Input = InputConstraintInfos[InputNo];
if (Input.hasTiedOperand() && Input.getTiedOperand() == i)
break;
}
assert(InputNo != S.getNumInputs() && "Didn't find matching input!");

QualType InputTy = S.getInputExpr(InputNo)->getType();
QualType OutputType = OutExpr->getType();

uint64_t InputSize = getContext().getTypeSize(InputTy);
if (getContext().getTypeSize(OutputType) < InputSize) {
// Form the asm to return the value as a larger integer or fp type.
ResultRegTypes.back() = ConvertType(InputTy);
}
}
if (mlir::Type AdjTy = getTargetHooks().adjustInlineAsmType(
*this, OutputConstraint, ResultRegTypes.back()))
ResultRegTypes.back() = AdjTy;
else {
CGM.getDiags().Report(S.getAsmLoc(),
diag::err_asm_invalid_type_in_input)
<< OutExpr->getType() << OutputConstraint;
}

// Update largest vector width for any vector types.
assert(!UnimplementedFeature::asm_vector_type());
} else {
Address DestAddr = Dest.getAddress();

Expand All @@ -323,16 +390,21 @@ mlir::LogicalResult CIRGenFunction::buildAsmStmt(const AsmStmt &S) {
if (isa<MatrixType>(OutExpr->getType().getCanonicalType()))
DestAddr = DestAddr.withElementType(ConvertType(OutExpr->getType()));

ArgTypes.push_back(DestAddr.getType());
ArgElemTypes.push_back(DestAddr.getElementType());
Args.push_back(DestAddr.getPointer());
Constraints += "=*";
Constraints += OutputConstraint;
ReadOnly = ReadNone = false;
}

if (Info.isReadWrite()) {
InOutConstraints += ',';
const Expr *InputExpr = S.getOutputExpr(i);

mlir::Value Arg =
mlir::Value Arg;
mlir::Type ArgElemType;
std::tie(Arg, ArgElemType) =
buildAsmInputLValue(Info, Dest, InputExpr->getType(),
InOutConstraints, InputExpr->getExprLoc());

Expand All @@ -346,6 +418,8 @@ mlir::LogicalResult CIRGenFunction::buildAsmStmt(const AsmStmt &S) {
else
InOutConstraints += OutputConstraint;

InOutArgTypes.push_back(Arg.getType());
InOutArgElemTypes.push_back(ArgElemType);
InOutArgs.push_back(Arg);
}
} // iterate over output operands
Expand All @@ -368,7 +442,9 @@ mlir::LogicalResult CIRGenFunction::buildAsmStmt(const AsmStmt &S) {
getTarget(), CGM, S, false /* No EarlyClobber */);

std::string ReplaceConstraint(InputConstraint);
mlir::Value Arg = buildAsmInput(Info, InputExpr, Constraints);
mlir::Value Arg;
mlir::Type ArgElemType;
std::tie(Arg, ArgElemType) = buildAsmInput(Info, InputExpr, Constraints);

// If this input argument is tied to a larger output result, extend the
// input to be the same size as the output. The LLVM backend wants to see
Expand Down Expand Up @@ -405,12 +481,16 @@ mlir::LogicalResult CIRGenFunction::buildAsmStmt(const AsmStmt &S) {
CGM.getDiags().Report(S.getAsmLoc(), diag::err_asm_invalid_type_in_input)
<< InputExpr->getType() << InputConstraint;

ArgTypes.push_back(Arg.getType());
ArgElemTypes.push_back(ArgElemType);
Args.push_back(Arg);
Constraints += InputConstraint;
} // iterate over input operands

// Append the "input" part of inout constraints.
for (unsigned i = 0, e = InOutArgs.size(); i != e; i++) {
ArgTypes.push_back(InOutArgTypes[i]);
ArgElemTypes.push_back(InOutArgElemTypes[i]);
Args.push_back(InOutArgs[i]);
}
Constraints += InOutConstraints;
Expand All @@ -430,9 +510,43 @@ mlir::LogicalResult CIRGenFunction::buildAsmStmt(const AsmStmt &S) {

bool HasSideEffect = S.isVolatile() || S.getNumOutputs() == 0;

builder.create<mlir::cir::InlineAsmOp>(getLoc(S.getAsmLoc()), ResultType,
Args, AsmString, Constraints,
HasSideEffect, inferFlavor(CGM, S));
auto IA = builder.create<mlir::cir::InlineAsmOp>(
getLoc(S.getAsmLoc()), ResultType, Args, AsmString, Constraints,
HasSideEffect, inferFlavor(CGM, S), mlir::ArrayAttr());

if (false /*IsGCCAsmGoto*/) {
assert(!UnimplementedFeature::asm_goto());
} else if (HasUnwindClobber) {
assert(!UnimplementedFeature::asm_unwind_clobber());
} else {
assert(!UnimplementedFeature::asm_memory_effects());

mlir::Value result;
if (IA.getNumResults())
result = IA.getResult(0);

std::vector<mlir::Attribute> operandAttrs;

// 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 (!IA.getNumResults())
operandAttrs.push_back(OptNoneAttr::get(builder.getContext()));

for (auto typ : ArgElemTypes) {
if (typ) {
operandAttrs.push_back(mlir::TypeAttr::get(typ));
} 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(OptNoneAttr::get(builder.getContext()));
}
}

IA.setOperandAttrsAttr(builder.getArrayAttr(operandAttrs));
}

return mlir::success();
}
15 changes: 8 additions & 7 deletions clang/lib/CIR/CodeGen/CIRGenFunction.h
Original file line number Diff line number Diff line change
Expand Up @@ -951,13 +951,14 @@ class CIRGenFunction : public CIRGenTypeCache {

mlir::LogicalResult buildAsmStmt(const clang::AsmStmt &S);

mlir::Value buildAsmInputLValue(const TargetInfo::ConstraintInfo &Info,
LValue InputValue, QualType InputType,
std::string &ConstraintStr,
SourceLocation Loc);

mlir::Value buildAsmInput(const TargetInfo::ConstraintInfo &Info,
const Expr *InputExpr, std::string &ConstraintStr);
std::pair<mlir::Value, mlir::Type>
buildAsmInputLValue(const TargetInfo::ConstraintInfo &Info, LValue InputValue,
QualType InputType, std::string &ConstraintStr,
SourceLocation Loc);

std::pair<mlir::Value, mlir::Type>
buildAsmInput(const TargetInfo::ConstraintInfo &Info, const Expr *InputExpr,
std::string &ConstraintStr);

mlir::LogicalResult buildIfStmt(const clang::IfStmt &S);

Expand Down
6 changes: 6 additions & 0 deletions clang/lib/CIR/CodeGen/UnimplementedFeatureGuarding.h
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,12 @@ struct UnimplementedFeature {
static bool escapedLocals() { return false; }
static bool deferredReplacements() { return false; }
static bool shouldInstrumentFunction() { return false; }

// Inline assembly
static bool asm_goto() { return false; }
static bool asm_unwind_clobber() { return false; }
static bool asm_memory_effects() { return false; }
static bool asm_vector_type() { return false; }
};
} // namespace cir

Expand Down
19 changes: 19 additions & 0 deletions clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2517,6 +2517,25 @@ class CIRInlineAsmOpLowering
: mlir::LLVM::AsmDialect::AD_Intel;

std::vector<mlir::Attribute> opAttrs;
auto llvmAttrName = mlir::LLVM::InlineAsmOp::getElementTypeAttrName();

if (auto operandAttrs = op.getOperandAttrs()) {
for (auto attr : *operandAttrs) {
if (isa<mlir::cir::OptNoneAttr>(attr)) {
opAttrs.push_back(mlir::Attribute());
continue;
}

mlir::TypeAttr tAttr = cast<mlir::TypeAttr>(attr);
std::vector<mlir::NamedAttribute> attrs;
auto typAttr = mlir::TypeAttr::get(
getTypeConverter()->convertType(tAttr.getValue()));

attrs.push_back(rewriter.getNamedAttr(llvmAttrName, typAttr));
auto newDict = rewriter.getDictionaryAttr(attrs);
opAttrs.push_back(newDict);
}
}

rewriter.replaceOpWithNewOp<mlir::LLVM::InlineAsmOp>(
op, llResTy, adaptor.getOperands(), op.getAsmStringAttr(),
Expand Down
Loading

0 comments on commit 44b5456

Please sign in to comment.