22#include "llvm/ADT/StringExtras.h"
23#include "llvm/Support/LogicalResult.h"
38static FailureOr<emitc::OpaqueType>
39getOrCreateMultiReturnType(ConversionPatternRewriter &rewriter,
Location loc,
43 std::string structName =
"return";
44 for (
Type type : types) {
46 llvm::raw_string_ostream os(typeName);
49 typeName.begin(), typeName.end(),
50 [](
char c) { return !llvm::isAlnum(c); },
'_');
51 structName +=
"_" + typeName;
58 while (insertBefore->
getParentOp() != symbolTableOp)
62 auto classOp = dyn_cast<emitc::ClassOp>(sym);
64 return emitError(loc) <<
"symbol '" << structName
65 <<
"' exists but is not an emitc.class";
67 if (classOp.getClassType() != emitc::ClassType::struct_)
69 <<
"existing class '" << structName <<
"' is not a struct";
72 for (
Operation &bodyOp : classOp.getBody().front()) {
73 if (isa<emitc::FuncOp>(bodyOp))
74 return emitError(loc) <<
"existing class '" << structName
75 <<
"' has methods; expected a plain struct";
76 if (
auto fieldOp = dyn_cast<emitc::FieldOp>(bodyOp))
77 fields.push_back(fieldOp);
79 if (fields.size() != types.size())
80 return emitError(loc) <<
"existing class '" << structName
81 <<
"' has wrong number of fields";
82 for (
auto [i, fieldOp] : llvm::enumerate(fields)) {
83 if (fieldOp.getSymName() !=
"field" + std::to_string(i))
84 return emitError(loc) <<
"existing class '" << structName
85 <<
"': unexpected field name at index " << i;
86 if (fieldOp.getTypeAttr().getValue() != types[i])
87 return emitError(loc) <<
"existing class '" << structName
88 <<
"': wrong type for field " << i;
93 auto savedIP = rewriter.saveInsertionPoint();
94 rewriter.setInsertionPoint(insertBefore);
96 emitc::ClassOp classOp = emitc::ClassOp::create(
97 rewriter, loc, structName,
nullptr,
98 false, emitc::ClassType::struct_);
99 rewriter.createBlock(&classOp.getBody());
100 rewriter.setInsertionPointToStart(&classOp.getBody().front());
102 for (
auto [i, type] : llvm::enumerate(types)) {
103 auto fieldName = rewriter.getStringAttr(
"field" + std::to_string(i));
104 emitc::FieldOp::create(rewriter, loc, fieldName,
105 nullptr, TypeAttr::get(type),
109 rewriter.restoreInsertionPoint(savedIP);
111 return emitc::OpaqueType::get(rewriter.getContext(),
"struct " + structName);
116static Value packValuesIntoStruct(ConversionPatternRewriter &rewriter,
118 emitc::OpaqueType structType) {
120 auto noInit = emitc::OpaqueAttr::get(ctx,
"");
122 emitc::VariableOp::create(rewriter, loc,
123 emitc::LValueType::get(structType), noInit)
125 for (
auto [i, val] : llvm::enumerate(values)) {
127 emitc::MemberOp::create(
128 rewriter, loc, emitc::LValueType::get(val.getType()),
129 rewriter.getStringAttr(
"field" + std::to_string(i)), structLv)
131 emitc::AssignOp::create(rewriter, loc, fieldLv, val);
133 return emitc::LoadOp::create(rewriter, loc, structType, structLv).getResult();
137struct FuncToEmitCDialectInterface :
public ConvertToEmitCPatternInterface {
138 FuncToEmitCDialectInterface(Dialect *dialect)
139 : ConvertToEmitCPatternInterface(dialect) {}
143 void populateConvertToEmitCConversionPatterns(
144 ConversionTarget &
target, TypeConverter &typeConverter,
145 RewritePatternSet &patterns, std::optional<bool> lowerToCpp)
const final {
147 lowerToCpp.value_or(
true));
154 dialect->addInterfaces<FuncToEmitCDialectInterface>();
163class CallOpConversion final :
public OpConversionPattern<func::CallOp> {
167 : OpConversionPattern<
func::CallOp>(typeConverter, ctx),
168 lowerToCpp(lowerToCpp) {}
171 matchAndRewrite(func::CallOp callOp, OpAdaptor adaptor,
172 ConversionPatternRewriter &rewriter)
const override {
175 if (callOp.getNumResults() > 1 && lowerToCpp)
176 return rewriter.notifyMatchFailure(
177 callOp,
"only functions with zero or one result can be converted");
179 SmallVector<Type> convertedResultTypes;
180 for (Type t : callOp.getResultTypes()) {
181 Type resultType = getTypeConverter()->convertType(t);
183 return rewriter.notifyMatchFailure(callOp,
184 "result type conversion failed");
185 if (isa<emitc::ArrayType>(resultType))
186 return rewriter.notifyMatchFailure(
187 callOp,
"function calls returning arrays are not supported");
188 convertedResultTypes.push_back(resultType);
191 if (callOp.getNumResults() <= 1) {
192 auto newCall = rewriter.replaceOpWithNewOp<emitc::CallOp>(
193 callOp, callOp.getCalleeAttr(), convertedResultTypes,
194 adaptor.getOperands());
195 newCall.setArgAttrsAttr(callOp.getArgAttrsAttr());
196 newCall.setResAttrsAttr(callOp.getResAttrsAttr());
197 newCall->setDiscardableAttrs(callOp->getDiscardableAttrDictionary());
202 Location loc = callOp.getLoc();
205 getOrCreateMultiReturnType(rewriter, loc, callOp, convertedResultTypes);
207 return rewriter.notifyMatchFailure(callOp,
208 "incompatible multi-return struct");
212 emitc::CallOp::create(rewriter, loc, callOp.getCalleeAttr(),
213 TypeRange{*structType}, adaptor.getOperands())
217 SmallVector<Value> results;
218 for (
auto [i,
result] : llvm::enumerate(callOp.getResults())) {
220 results.push_back(Value());
223 Type fieldType = convertedResultTypes[i];
224 StringAttr fieldName =
225 rewriter.getStringAttr(
"field" + std::to_string(i));
226 Value fieldValue = emitc::MemberOp::create(rewriter, loc, fieldType,
227 fieldName, structVal)
229 results.push_back(fieldValue);
232 rewriter.replaceOp(callOp, results);
240class FuncOpConversion final :
public OpConversionPattern<func::FuncOp> {
242 FuncOpConversion(
const TypeConverter &typeConverter, MLIRContext *ctx,
244 : OpConversionPattern<func::FuncOp>(typeConverter, ctx),
245 lowerToCpp(lowerToCpp) {}
248 matchAndRewrite(func::FuncOp funcOp, OpAdaptor adaptor,
249 ConversionPatternRewriter &rewriter)
const override {
250 FunctionType fnType = funcOp.getFunctionType();
254 if (fnType.getNumResults() > 1 && lowerToCpp)
255 return rewriter.notifyMatchFailure(
256 funcOp,
"only functions with zero or one result can be converted");
258 TypeConverter::SignatureConversion signatureConverter(
259 fnType.getNumInputs());
260 for (
const auto &argType :
enumerate(fnType.getInputs())) {
261 auto convertedType = getTypeConverter()->convertType(argType.value());
263 return rewriter.notifyMatchFailure(funcOp,
264 "argument type conversion failed");
265 signatureConverter.addInputs(argType.index(), convertedType);
268 SmallVector<Type> convertedResultTypes;
269 for (Type t : fnType.getResults()) {
270 Type resultType = getTypeConverter()->convertType(t);
272 return rewriter.notifyMatchFailure(funcOp,
273 "result type conversion failed");
274 if (isa<emitc::ArrayType>(resultType))
275 return rewriter.notifyMatchFailure(
276 funcOp,
"functions returning arrays are not supported");
277 convertedResultTypes.push_back(resultType);
281 if (fnType.getNumResults() == 1) {
282 resultType = convertedResultTypes[0];
283 }
else if (fnType.getNumResults() > 1) {
284 auto structTypeOrErr = getOrCreateMultiReturnType(
285 rewriter, funcOp.getLoc(), funcOp, convertedResultTypes);
286 if (
failed(structTypeOrErr))
287 return rewriter.notifyMatchFailure(funcOp,
288 "incompatible multi-return struct");
289 resultType = *structTypeOrErr;
293 emitc::FuncOp newFuncOp = emitc::FuncOp::create(
294 rewriter, funcOp.getLoc(), funcOp.getName(),
295 FunctionType::get(rewriter.getContext(),
296 signatureConverter.getConvertedTypes(),
299 newFuncOp.setArgAttrsAttr(funcOp.getArgAttrsAttr());
300 newFuncOp.setResAttrsAttr(funcOp.getResAttrsAttr());
301 newFuncOp.setVisibility(funcOp.getVisibility());
304 for (
const auto &namedAttr :
305 funcOp->getDiscardableAttrDictionary().getValue())
306 newFuncOp->setDiscardableAttr(namedAttr.getName(), namedAttr.getValue());
309 if (funcOp.isDeclaration()) {
310 ArrayAttr specifiers = rewriter.getStrArrayAttr({
"extern"});
311 newFuncOp.setSpecifiersAttr(specifiers);
316 if (funcOp.isPrivate() && !funcOp.isDeclaration()) {
317 ArrayAttr specifiers = rewriter.getStrArrayAttr({
"static"});
318 newFuncOp.setSpecifiersAttr(specifiers);
321 if (!funcOp.isDeclaration()) {
322 rewriter.inlineRegionBefore(funcOp.getBody(), newFuncOp.getBody(),
324 if (
failed(rewriter.convertRegionTypes(
325 &newFuncOp.getBody(), *getTypeConverter(), &signatureConverter)))
328 rewriter.eraseOp(funcOp);
337class ReturnOpConversion final :
public OpConversionPattern<func::ReturnOp> {
339 ReturnOpConversion(
const TypeConverter &typeConverter, MLIRContext *ctx,
341 : OpConversionPattern<func::ReturnOp>(typeConverter, ctx),
342 lowerToCpp(lowerToCpp) {}
345 matchAndRewrite(func::ReturnOp returnOp, OpAdaptor adaptor,
346 ConversionPatternRewriter &rewriter)
const override {
349 if (returnOp.getNumOperands() > 1 && lowerToCpp)
350 return rewriter.notifyMatchFailure(
351 returnOp,
"only zero or one operand is supported");
353 if (llvm::any_of(adaptor.getOperands(), [](Value operand) {
354 return isa<emitc::ArrayType>(operand.getType());
356 return rewriter.notifyMatchFailure(returnOp,
357 "returning arrays is not supported");
359 if (returnOp.getNumOperands() <= 1) {
360 rewriter.replaceOpWithNewOp<emitc::ReturnOp>(
362 returnOp.getNumOperands() ? adaptor.getOperands()[0] :
nullptr);
367 Location loc = returnOp.getLoc();
369 auto structType = getOrCreateMultiReturnType(
370 rewriter, loc, returnOp, adaptor.getOperands().getTypes());
372 return rewriter.notifyMatchFailure(returnOp,
373 "incompatible multi-return struct");
376 packValuesIntoStruct(rewriter, loc, adaptor.getOperands(), *structType);
377 rewriter.replaceOpWithNewOp<emitc::ReturnOp>(returnOp, structVal);
395 patterns.
add<CallOpConversion, FuncOpConversion, ReturnOpConversion>(
396 typeConverter, ctx, lowerToCpp);
The DialectRegistry maps a dialect namespace to a constructor for the matching dialect.
bool addExtension(TypeID extensionID, std::unique_ptr< DialectExtensionBase > extension)
Add the given extension to the registry.
This class defines the main interface for locations in MLIR and acts as a non-nullable wrapper around...
MLIRContext is the top-level object for a collection of MLIR operations.
Operation is the basic unit of execution within MLIR.
Operation * getParentOp()
Returns the closest surrounding operation that contains this operation or nullptr if this is a top-le...
MLIRContext * getContext() const
RewritePatternSet & add(ConstructorArg &&arg, ConstructorArgs &&...args)
Add an instance of each of the pattern types 'Ts' to the pattern list with the given arguments.
static Operation * lookupSymbolIn(Operation *op, StringAttr symbol)
Returns the operation registered with the given symbol name with the regions of 'symbolTableOp'.
static Operation * getNearestSymbolTable(Operation *from)
Returns the nearest symbol table from a given operation from.
This class provides an abstraction over the various different ranges of value types.
Instances of the Type class are uniqued, have an immutable identifier and an optional mutable compone...
This class provides an abstraction over the different types of ranges over Values.
This class represents an instance of an SSA value in the MLIR system, representing a computable value...
constexpr void enumerate(std::tuple< Tys... > &tuple, CallbackT &&callback)
Include the generated interface declarations.
void populateFuncToEmitCPatterns(const TypeConverter &typeConverter, RewritePatternSet &patterns, bool lowerToCpp=true)
void registerConvertFuncToEmitCInterface(DialectRegistry ®istry)
InFlightDiagnostic emitError(Location loc)
Utility method to emit an error message using this location.