Changeset View
Standalone View
mlir/lib/Dialect/LLVMIR/IR/LLVMDialect.cpp
Show First 20 Lines • Show All 3,114 Lines • ▼ Show 20 Lines | LogicalResult LLVMDialect::verifyStructAttr(Operation *op, Attribute attr, | ||||
if (structType.getBody().size() != arrAttrs.size()) | if (structType.getBody().size() != arrAttrs.size()) | ||||
return op->emitError() | return op->emitError() | ||||
<< "size of '" << LLVMDialect::getStructAttrsAttrName() | << "size of '" << LLVMDialect::getStructAttrsAttrName() | ||||
<< "' must match the size of the annotated '!llvm.struct'"; | << "' must match the size of the annotated '!llvm.struct'"; | ||||
return success(); | return success(); | ||||
} | } | ||||
static LogicalResult verifyFuncOpInterfaceStructAttr( | static LogicalResult verifyFuncOpInterfaceStructAttr(Operation *op, | ||||
Operation *op, Attribute attr, | Attribute attr, | ||||
const std::function<Type(FunctionOpInterface)> &getAnnotatedType) { | Type annotatedType) { | ||||
if (auto funcOp = dyn_cast<FunctionOpInterface>(op)) | if (isa<FunctionOpInterface>(op)) | ||||
return LLVMDialect::verifyStructAttr(op, attr, getAnnotatedType(funcOp)); | return LLVMDialect::verifyStructAttr(op, attr, annotatedType); | ||||
return op->emitError() << "expected '" | return op->emitError() << "expected '" | ||||
<< LLVMDialect::getStructAttrsAttrName() | << LLVMDialect::getStructAttrsAttrName() | ||||
<< "' to be used on function-like operations"; | << "' to be used on function-like operations"; | ||||
} | } | ||||
/// Verify LLVMIR function argument attributes. | LogicalResult LLVMDialect::verifyParameterAttribute(Operation *op, | ||||
LogicalResult LLVMDialect::verifyRegionArgAttribute(Operation *op, | Type paramType, | ||||
unsigned regionIdx, | NamedAttribute paramAttr) { | ||||
unsigned argIdx, | // LLVM attribute may be attached to a result of operation that has not been | ||||
gysit: Let's take over the original comment that explains that the operation result types may be in a… | |||||
NamedAttribute argAttr) { | // converted to LLVM dialect yet, so the result may have a type with unknown | ||||
// Check that llvm.noalias is a unit attribute. | // representation in LLVM dialect type space. In this case we cannot verify | ||||
if (argAttr.getName() == LLVMDialect::getNoAliasAttrName() && | // whether the attribute may be | ||||
!argAttr.getValue().isa<UnitAttr>()) | bool verifyValueType = isCompatibleType(paramType); | ||||
StringAttr name = paramAttr.getName(); | |||||
auto checkUnitAttrType = [&]() -> LogicalResult { | |||||
if (!paramAttr.getValue().isa<UnitAttr>()) | |||||
return op->emitError() << name << " should be a unit attribute"; | |||||
return success(); | |||||
}; | |||||
auto checkTypeAttrType = [&]() -> LogicalResult { | |||||
if (!paramAttr.getValue().isa<TypeAttr>()) | |||||
return op->emitError() << name << " should be a type attribute"; | |||||
return success(); | |||||
}; | |||||
auto checkIntegerAttrType = [&]() -> LogicalResult { | |||||
if (!paramAttr.getValue().isa<IntegerAttr>()) | |||||
return op->emitError() << name << " should be an integer attribute"; | |||||
return success(); | |||||
}; | |||||
auto checkPointerType = [&]() -> LogicalResult { | |||||
if (!paramType.isa<LLVMPointerType>()) | |||||
return op->emitError() | return op->emitError() | ||||
<< "expected llvm.noalias argument attribute to be a unit attribute"; | << name << " attribute attached to non-pointer LLVM type"; | ||||
// Check that llvm.align is an integer attribute. | return success(); | ||||
if (argAttr.getName() == LLVMDialect::getAlignAttrName() && | }; | ||||
!argAttr.getValue().isa<IntegerAttr>()) | auto checkIntegerType = [&]() -> LogicalResult { | ||||
if (!paramType.isa<IntegerType>()) | |||||
return op->emitError() | return op->emitError() | ||||
<< "llvm.align argument attribute of non integer type"; | << name << " attribute attached to non-integer LLVM type"; | ||||
if (argAttr.getName() == LLVMDialect::getStructAttrsAttrName()) { | |||||
return verifyFuncOpInterfaceStructAttr( | |||||
op, argAttr.getValue(), [argIdx](FunctionOpInterface funcOp) { | |||||
return funcOp.getArgumentTypes()[argIdx]; | |||||
}); | |||||
} | |||||
return success(); | return success(); | ||||
} | }; | ||||
auto checkPointerTypeMatches = [&]() -> LogicalResult { | |||||
LogicalResult LLVMDialect::verifyRegionResultAttribute(Operation *op, | if (failed(checkPointerType())) | ||||
unsigned regionIdx, | return failure(); | ||||
unsigned resIdx, | auto ptrType = paramType.cast<LLVMPointerType>(); | ||||
NamedAttribute resAttr) { | auto typeAttr = paramAttr.getValue().cast<TypeAttr>(); | ||||
StringAttr name = resAttr.getName(); | |||||
if (name == LLVMDialect::getStructAttrsAttrName()) { | |||||
return verifyFuncOpInterfaceStructAttr( | |||||
op, resAttr.getValue(), [resIdx](FunctionOpInterface funcOp) { | |||||
return funcOp.getResultTypes()[resIdx]; | |||||
}); | |||||
} | |||||
if (auto funcOp = dyn_cast<FunctionOpInterface>(op)) { | |||||
mlir::Type resTy = funcOp.getResultTypes()[resIdx]; | |||||
// Check to see if this function has a void return with a result attribute | if (!ptrType.isOpaque() && ptrType.getElementType() != typeAttr.getValue()) | ||||
// to it. It isn't clear what semantics we would assign to that. | return op->emitError() | ||||
if (resTy.isa<LLVMVoidType>()) | << name | ||||
return op->emitError() << "cannot attach result attributes to functions " | << " attribute attached to LLVM pointer argument of " | ||||
"with a void return"; | "different type"; | ||||
return success(); | |||||
}; | |||||
There may be downstream users for this functionality. I would keep the functionality and change the comment to "Note: The struct parameter attributes are not lowered to LLVM IR." or similar. gysit: There may be downstream users for this functionality. I would keep the functionality and change… | |||||
@laszlo-luminous (or @laszlokindrat) you initially pushed the struct_attr for C-wrappers. Do you know of any downstream usage of this functionality? The struct_attr functionality is not an LLVM feature and will thus be thrown away when it's exported to LLVM IR. Dinistro: @laszlo-luminous (or @laszlokindrat) you initially pushed the `struct_attr` for C-wrappers. Do… | |||||
@laszlokindrat is on vacation and doesn't have access, so he asked me to respond since I worked with him on this. It's been a very long time so I don't remember everything, but I remember we made these changes to fix a bug in convert-func-to-llvm=emit-c-wrappers back when the pass used to have the emit-c-wrappers option. Here's the issue I posted about it (can't remember why I'm talking about convert-std-to-llvm at first) https://github.com/llvm/llvm-project/issues/53503. Unfortunately I don't remember anything about struct_attr, but the main issue we fixed was that arg_attrs wasn't being updated correctly. So none of this was specifically intended for downstream uses, but rather a bug fix. srcarroll: @laszlokindrat is on vacation and doesn't have access, so he asked me to respond since I worked… | |||||
Thanks for the response @srcarroll. In that case the struct_attr was probably introduced with the goal of not dropping any information. Unfortunately, this is not supported by LLVM and is therefore ignored while lowering. As there seems to be no downstream usage, I will probably clean this up once this patch has landed.
The pass was originally named std-to-llvm ;) Dinistro: Thanks for the response @srcarroll. In that case the `struct_attr` was probably introduced with… | |||||
// LLVM attribute may be attached to a result of operation | // Note: The struct parameter attributes are not lowered to LLVM IR. | ||||
// that has not been converted to LLVM dialect yet, so the result | if (name == LLVMDialect::getStructAttrsAttrName()) | ||||
// may have a type with unknown representation in LLVM dialect type | return verifyFuncOpInterfaceStructAttr(op, paramAttr.getValue(), paramType); | ||||
// space. In this case we cannot verify whether the attribute may be | |||||
// attached to a result of such type. | |||||
bool verifyValueType = isCompatibleType(resTy); | |||||
Attribute attrValue = resAttr.getValue(); | |||||
// TODO: get rid of code duplication here and in verifyRegionArgAttribute(). | // Check a unit attribute that is attached to a pointer value. | ||||
if (name == LLVMDialect::getAlignAttrName()) { | if (name == LLVMDialect::getNoAliasAttrName() || | ||||
if (!attrValue.isa<IntegerAttr>()) | name == LLVMDialect::getReadonlyAttrName() || | ||||
return op->emitError() << "expected llvm.align result attribute to be " | name == LLVMDialect::getNestAttrName()) { | ||||
"an integer attribute"; | if (failed(checkUnitAttrType())) | ||||
if (verifyValueType && !resTy.isa<LLVMPointerType>()) | return failure(); | ||||
return op->emitError() | if (verifyValueType && failed(checkPointerType())) | ||||
<< "llvm.align attribute attached to non-pointer result"; | return failure(); | ||||
return success(); | return success(); | ||||
} | } | ||||
if (name == LLVMDialect::getNoAliasAttrName()) { | |||||
if (!attrValue.isa<UnitAttr>()) | // Check a type attribute that is attached to a pointer value. | ||||
return op->emitError() << "expected llvm.noalias result attribute to " | if (name == LLVMDialect::getStructRetAttrName() || | ||||
"be a unit attribute"; | name == LLVMDialect::getByValAttrName() || | ||||
if (verifyValueType && !resTy.isa<LLVMPointerType>()) | name == LLVMDialect::getByRefAttrName() || | ||||
return op->emitError() | name == LLVMDialect::getInAllocaAttrName()) { | ||||
<< "llvm.noalias attribute attached to non-pointer result"; | if (failed(checkTypeAttrType())) | ||||
return failure(); | |||||
if (verifyValueType && failed(checkPointerTypeMatches())) | |||||
return failure(); | |||||
return success(); | return success(); | ||||
} | } | ||||
if (name == LLVMDialect::getReadonlyAttrName()) { | |||||
nit: unit gysit: nit: unit | |||||
if (!attrValue.isa<UnitAttr>()) | // Check a unit attribute that is attached to an integer value. | ||||
return op->emitError() << "expected llvm.readonly result attribute to " | if (name == LLVMDialect::getSExtAttrName() || | ||||
"be a unit attribute"; | name == LLVMDialect::getZExtAttrName()) { | ||||
if (verifyValueType && !resTy.isa<LLVMPointerType>()) | if (failed(checkUnitAttrType())) | ||||
return op->emitError() | return failure(); | ||||
<< "llvm.readonly attribute attached to non-pointer result"; | if (verifyValueType && failed(checkIntegerType())) | ||||
return failure(); | |||||
return success(); | return success(); | ||||
} | } | ||||
if (name == LLVMDialect::getNoUndefAttrName()) { | |||||
if (!attrValue.isa<UnitAttr>()) | // Check an integer attribute that is attached to a pointer value. | ||||
return op->emitError() << "expected llvm.noundef result attribute to " | if (name == LLVMDialect::getAlignAttrName()) { | ||||
"be a unit attribute"; | if (failed(checkIntegerAttrType())) | ||||
return failure(); | |||||
if (verifyValueType && failed(checkPointerType())) | |||||
return failure(); | |||||
return success(); | return success(); | ||||
} | } | ||||
if (name == LLVMDialect::getSExtAttrName()) { | |||||
if (!attrValue.isa<UnitAttr>()) | if (name == LLVMDialect::getNoUndefAttrName()) | ||||
return op->emitError() << "expected llvm.signext result attribute to " | return checkUnitAttrType(); | ||||
"be a unit attribute"; | |||||
if (verifyValueType && !resTy.isa<mlir::IntegerType>()) | |||||
return op->emitError() | |||||
<< "llvm.signext attribute attached to non-integer result"; | |||||
return success(); | return success(); | ||||
} | } | ||||
if (name == LLVMDialect::getZExtAttrName()) { | |||||
if (!attrValue.isa<UnitAttr>()) | /// Verify LLVMIR function argument attributes. | ||||
return op->emitError() << "expected llvm.zeroext result attribute to " | LogicalResult LLVMDialect::verifyRegionArgAttribute(Operation *op, | ||||
"be a unit attribute"; | unsigned regionIdx, | ||||
if (verifyValueType && !resTy.isa<mlir::IntegerType>()) | unsigned argIdx, | ||||
return op->emitError() | NamedAttribute argAttr) { | ||||
<< "llvm.zeroext attribute attached to non-integer result"; | auto funcOp = dyn_cast<FunctionOpInterface>(op); | ||||
if (!funcOp) | |||||
return success(); | return success(); | ||||
} | Type argType = funcOp.getArgumentTypes()[argIdx]; | ||||
return verifyParameterAttribute(op, argType, argAttr); | |||||
} | } | ||||
LogicalResult LLVMDialect::verifyRegionResultAttribute(Operation *op, | |||||
unsigned regionIdx, | |||||
unsigned resIdx, | |||||
NamedAttribute resAttr) { | |||||
auto funcOp = dyn_cast<FunctionOpInterface>(op); | |||||
if (!funcOp) | |||||
return success(); | return success(); | ||||
Type resType = funcOp.getResultTypes()[resIdx]; | |||||
// Check to see if this function has a void return with a result attribute | |||||
// to it. It isn't clear what semantics we would assign to that. | |||||
if (resType.isa<LLVMVoidType>()) | |||||
return op->emitError() << "cannot attach result attributes to functions " | |||||
"with a void return"; | |||||
// Check to see if this attribute is allowed as a result attribute. Only | |||||
// explicitly forbidden LLVM attributes will cause an error. | |||||
auto name = resAttr.getName(); | |||||
if (name == LLVMDialect::getReadonlyAttrName() || | |||||
name == LLVMDialect::getNestAttrName() || | |||||
name == LLVMDialect::getStructRetAttrName() || | |||||
name == LLVMDialect::getByValAttrName() || | |||||
name == LLVMDialect::getByRefAttrName() || | |||||
name == LLVMDialect::getInAllocaAttrName()) | |||||
return op->emitError() << name << " is not a valid result attribute"; | |||||
return verifyParameterAttribute(op, resType, resAttr); | |||||
} | } | ||||
//===----------------------------------------------------------------------===// | //===----------------------------------------------------------------------===// | ||||
// Utility functions. | // Utility functions. | ||||
//===----------------------------------------------------------------------===// | //===----------------------------------------------------------------------===// | ||||
Value mlir::LLVM::createGlobalString(Location loc, OpBuilder &builder, | Value mlir::LLVM::createGlobalString(Location loc, OpBuilder &builder, | ||||
StringRef name, StringRef value, | StringRef name, StringRef value, | ||||
Show All 27 Lines |
Let's take over the original comment that explains that the operation result types may be in a transitioning state.