MLIR 24.0.0git
WasmSSAOps.cpp
Go to the documentation of this file.
1//===- WasmSSAOps.cpp - WasmSSA dialect operations ----------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===---------------------------------------------------------------------===//
8
11
12#include "mlir/IR/Attributes.h"
13#include "mlir/IR/Builders.h"
15#include "mlir/IR/Diagnostics.h"
16#include "mlir/IR/Dialect.h"
17#include "mlir/IR/Region.h"
18#include "mlir/IR/SymbolTable.h"
20#include "llvm/Support/Casting.h"
21
22//===----------------------------------------------------------------------===//
23// TableGen'd op method definitions
24//===----------------------------------------------------------------------===//
25
26using namespace mlir;
27namespace {
28ParseResult parseElseRegion(OpAsmParser &opParser, Region &elseRegion) {
29 std::string keyword;
30 std::ignore = opParser.parseOptionalKeywordOrString(&keyword);
31 if (keyword == "else")
32 return opParser.parseRegion(elseRegion);
33 return ParseResult::success();
34}
35
36void printElseRegion(OpAsmPrinter &opPrinter, Operation *op,
37 Region &elseRegion) {
38 if (elseRegion.empty())
39 return;
40 opPrinter.printKeywordOrString("else ");
41 opPrinter.printRegion(elseRegion);
42}
43} // namespace
44
45#define GET_OP_CLASSES
46#include "mlir/Dialect/WasmSSA/IR/WasmSSAOps.cpp.inc"
47
49#include "mlir/IR/Types.h"
50#include "llvm/Support/LogicalResult.h"
51
52using namespace wasmssa;
53
54namespace {
55inline LogicalResult
56inferTeeGetResType(ValueRange operands,
57 SmallVectorImpl<Type> &inferredReturnTypes) {
58 if (operands.empty())
59 return failure();
60 auto opType = dyn_cast<LocalRefType>(operands.front().getType());
61 if (!opType)
62 return failure();
63 inferredReturnTypes.push_back(opType.getElementType());
64 return success();
65}
66
67ParseResult parseImportOp(OpAsmParser &parser, OperationState &result) {
68 std::string importName;
69 auto *ctx = parser.getContext();
70 ParseResult res = parser.parseString(&importName);
71 result.addAttribute("importName", StringAttr::get(ctx, importName));
72
73 std::string fromStr;
74 res = parser.parseKeywordOrString(&fromStr);
75 if (failed(res) || fromStr != "from")
76 return failure();
77
78 std::string moduleName;
79 res = parser.parseString(&moduleName);
80 if (failed(res))
81 return failure();
82 result.addAttribute("moduleName", StringAttr::get(ctx, moduleName));
83
84 std::string asStr;
85 res = parser.parseKeywordOrString(&asStr);
86 if (failed(res) || asStr != "as")
87 return failure();
88
89 StringAttr symbolName;
90 res = parser.parseSymbolName(symbolName);
91 if (succeeded(res))
92 result.getOrAddProperties<GlobalImportOp::Properties>().sym_name =
93 symbolName;
94 return res;
95}
96} // namespace
97
98//===----------------------------------------------------------------------===//
99// BlockOp
100//===----------------------------------------------------------------------===//
101
102Block *BlockOp::getLabelTarget() { return getTarget(); }
103
104//===----------------------------------------------------------------------===//
105// BlockReturnOp
106//===----------------------------------------------------------------------===//
107
108std::size_t BlockReturnOp::getExitLevel() { return 0; }
109
110Block *BlockReturnOp::getTarget() {
111 return cast<LabelBranchingOpInterface>(getOperation())
112 .getTargetOp()
113 .getOperation()
114 ->getSuccessor(0);
115}
116
117//===----------------------------------------------------------------------===//
118// ExtendLowBitsSOp
119//===----------------------------------------------------------------------===//
120
121LogicalResult ExtendLowBitsSOp::verify() {
122 auto bitsToTake = getBitsToTake().getValue().getLimitedValue();
123 if (bitsToTake != 32 && bitsToTake != 16 && bitsToTake != 8)
124 return emitError("extend op can only take 8, 16 or 32 bits. Got ")
125 << bitsToTake;
126
127 if (bitsToTake >= getInput().getType().getIntOrFloatBitWidth())
128 return emitError("trying to extend the ")
129 << bitsToTake << " low bits from a " << getInput().getType()
130 << " value is illegal";
131 return success();
132}
133
134//===----------------------------------------------------------------------===//
135// FuncOp
136//===----------------------------------------------------------------------===//
137
138Block *FuncOp::addEntryBlock() {
139 if (!getBody().empty()) {
140 emitError("adding entry block to a FuncOp which already has one");
141 return &getBody().front();
142 }
143 Block &block = getBody().emplaceBlock();
144 for (auto argType : getFunctionType().getInputs())
145 block.addArgument(LocalRefType::get(argType), getLoc());
146 return &block;
147}
148
149void FuncOp::build(OpBuilder &odsBuilder, OperationState &odsState,
150 StringRef symbol, FunctionType funcType) {
151 FuncOp::build(odsBuilder, odsState, symbol, funcType, {}, {});
152}
153
154ParseResult FuncOp::parse(OpAsmParser &parser, OperationState &result) {
155 auto *ctx = parser.getContext();
156 std::string visibilityString;
157 auto loc = parser.getNameLoc();
158 ParseResult res = parser.parseOptionalKeywordOrString(&visibilityString);
159 bool exported{false};
160 if (res.succeeded()) {
161 if (visibilityString != "exported")
162 return parser.emitError(
163 loc, "expecting either `exported` or symbol name. got ")
164 << visibilityString;
165 exported = true;
166 }
167
168 auto buildFuncType = [&parser](Builder &builder, ArrayRef<Type> argTypes,
169 ArrayRef<Type> results,
171 std::string &) {
172 SmallVector<Type> argTypesWithoutLocal{};
173 argTypesWithoutLocal.reserve(argTypes.size());
174 llvm::for_each(argTypes, [&parser, &argTypesWithoutLocal](Type argType) {
175 auto refType = dyn_cast<LocalRefType>(argType);
176 auto loc = parser.getEncodedSourceLoc(parser.getCurrentLocation());
177 if (!refType) {
178 mlir::emitError(loc, "invalid type for wasm.func argument. Expecting "
179 "!wasm<local T>, got ")
180 << argType;
181 return;
182 }
183 argTypesWithoutLocal.push_back(refType.getElementType());
184 });
185
186 return builder.getFunctionType(argTypesWithoutLocal, results);
187 };
189 parser, result, /*allowVariadic=*/false,
190 getFunctionTypeAttrName(result.name), buildFuncType,
191 getArgAttrsAttrName(result.name), getResAttrsAttrName(result.name));
192 if (exported)
193 result.addAttribute(getExportedAttrName(result.name), UnitAttr::get(ctx));
194 return funcParseRes;
195}
196
197LogicalResult FuncOp::verifyBody() {
198 if (getBody().empty())
199 return success();
200 Block &entry = getBody().front();
201 if (entry.getNumArguments() != getFunctionType().getNumInputs())
202 return emitError("entry block should have same number of arguments as "
203 "function type. Function type has ")
204 << getFunctionType().getNumInputs() << ", entry block has "
205 << entry.getNumArguments();
206
207 for (auto [argNo, funcSignatureType, blockType] : llvm::enumerate(
208 getFunctionType().getInputs(), entry.getArgumentTypes())) {
209 auto blockLocalRefType = dyn_cast<LocalRefType>(blockType);
210 if (!blockLocalRefType)
211 return emitError("entry block argument type should be LocalRefType, got ")
212 << blockType << " for block argument " << argNo;
213 if (blockLocalRefType.getElementType() != funcSignatureType)
214 return emitError("func argument type #")
215 << argNo << "(" << funcSignatureType
216 << ") doesn't match entry block referenced type ("
217 << blockLocalRefType.getElementType() << ")";
218 }
219 return success();
220}
221
222void FuncOp::print(OpAsmPrinter &p) {
223 /// If exported, print it before and mask it before printing
224 /// using generic interface.
225 auto exported = getExported();
226 if (exported) {
227 p << " exported";
228 removeExportedAttr();
229 }
231 p, *this, /*isVariadic=*/false, getFunctionTypeAttrName(),
232 getArgAttrsAttrName(), getResAttrsAttrName());
233 if (exported)
234 setExported(true);
235}
236
237//===----------------------------------------------------------------------===//
238// FuncImportOp
239//===----------------------------------------------------------------------===//
240
241void FuncImportOp::build(OpBuilder &odsBuilder, OperationState &odsState,
242 StringRef symbol, StringRef moduleName,
243 StringRef importName, FunctionType type) {
244 FuncImportOp::build(odsBuilder, odsState, symbol, moduleName, importName,
245 type, {}, {});
246}
247
248//===----------------------------------------------------------------------===//
249// GlobalOp
250//===----------------------------------------------------------------------===//
251namespace {
252Operation *getGlobalOpTerminatorOp(GlobalOp gop) {
253 return gop.getInitializer().begin()->getTerminator();
254}
255} // namespace
256
257ReturnOp GlobalOp::getInitTerminator() {
258 return llvm::cast<wasmssa::ReturnOp>(getGlobalOpTerminatorOp(*this));
259}
260
261// Custom formats
262ParseResult GlobalOp::parse(OpAsmParser &parser, OperationState &result) {
263 StringAttr symbolName;
264 Type globalType;
265 auto *ctx = parser.getContext();
266 std::string visibilityString;
267 auto loc = parser.getNameLoc();
268 ParseResult res = parser.parseOptionalKeywordOrString(&visibilityString);
269 if (res.succeeded()) {
270 if (visibilityString != "exported")
271 return parser.emitError(
272 loc, "expecting either `exported` or symbol name. got ")
273 << visibilityString;
274 result.addAttribute(getExportedAttrName(result.name), UnitAttr::get(ctx));
275 }
276
277 res = parser.parseSymbolName(symbolName, getSymNameAttrName(result.name),
278 result.attributes);
279 res = parser.parseType(globalType);
280 result.addAttribute(getTypeAttrName(result.name), TypeAttr::get(globalType));
281 std::string mutableString;
282 res = parser.parseOptionalKeywordOrString(&mutableString);
283 if (res.succeeded() && mutableString == "mutable")
284 result.addAttribute("isMutable", UnitAttr::get(ctx));
285
286 res = parser.parseColon();
287 Region *globalInitRegion = result.addRegion();
288 res = parser.parseRegion(*globalInitRegion);
289 return res;
290}
291
292void GlobalOp::print(OpAsmPrinter &printer) {
293 if (getExported())
294 printer << " exported";
295 printer << " @" << getSymName().str() << " " << getType();
296 if (getIsMutable())
297 printer << " mutable";
298 printer << " :";
299 Region &body = getRegion();
300 if (!body.empty()) {
301 printer << ' ';
302 printer.printRegion(body, /*printEntryBlockArgs=*/false,
303 /*printBlockTerminators=*/true);
304 }
305}
306
307LogicalResult GlobalOp::verify() {
308 return success(llvm::isa<ReturnOp>(getGlobalOpTerminatorOp(*this)));
309}
310
311//===----------------------------------------------------------------------===//
312// GlobalGetOp
313//===----------------------------------------------------------------------===//
314
315LogicalResult
316GlobalGetOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
317 // If the parent requires a constant context, verify that global.get is a
318 // constant as defined per the wasm standard.
319 if (!this->getOperation()
320 ->getParentWithTrait<ConstantExpressionInitializerOpTrait>())
321 return success();
323 StringRef referencedSymbol = getGlobal();
324 Operation *definitionOp = symbolTable.lookupSymbolIn(
325 symTabOp, StringAttr::get(this->getContext(), referencedSymbol));
326 if (!definitionOp)
327 return emitError() << "symbol @" << referencedSymbol << " is undefined";
328 auto definitionImport = dyn_cast<GlobalImportOp>(definitionOp);
329 if (!definitionImport || definitionImport.getIsMutable()) {
330 return emitError("global.get op is considered constant if it's referring "
331 "to a import.global symbol marked non-mutable");
332 }
333 return success();
334}
335
336//===----------------------------------------------------------------------===//
337// GlobalSetOp
338//===----------------------------------------------------------------------===//
339
340LogicalResult
341GlobalSetOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
343 StringRef referencedSymbol = getGlobal();
344 Operation *definitionOp = symbolTable.lookupSymbolIn(
345 symTabOp, StringAttr::get(this->getContext(), referencedSymbol));
346 if (!definitionOp)
347 return emitError() << "symbol @" << referencedSymbol << " is undefined";
348
349 Type globalType;
350 bool isMutable = false;
351 if (auto global = dyn_cast<GlobalOp>(definitionOp)) {
352 globalType = global.getType();
353 isMutable = global.getIsMutable();
354 } else if (auto globalImport = dyn_cast<GlobalImportOp>(definitionOp)) {
355 globalType = globalImport.getType();
356 isMutable = globalImport.getIsMutable();
357 } else {
358 return emitError() << "symbol @" << referencedSymbol
359 << " is not a global symbol";
360 }
361
362 if (!isMutable)
363 return emitError("global.set target must be mutable");
364
365 Type valueType = getValue().getType();
366 if (globalType != valueType)
367 return emitError("global.set value type does not match target global "
368 "type: expected ")
369 << globalType << " but got " << valueType;
370
371 return success();
372}
373
374//===----------------------------------------------------------------------===//
375// GlobalImportOp
376//===----------------------------------------------------------------------===//
377
378ParseResult GlobalImportOp::parse(OpAsmParser &parser, OperationState &result) {
379 auto *ctx = parser.getContext();
380 ParseResult res = parseImportOp(parser, result);
381 if (res.failed())
382 return failure();
383 std::string mutableOrSymVisString;
384 res = parser.parseOptionalKeywordOrString(&mutableOrSymVisString);
385 if (res.succeeded() && mutableOrSymVisString == "mutable") {
386 result.addAttribute("isMutable", UnitAttr::get(ctx));
387 }
388
389 res = parser.parseColon();
390
391 Type importedType;
392 res = parser.parseType(importedType);
393 if (res.succeeded())
394 result.addAttribute(getTypeAttrName(result.name),
395 TypeAttr::get(importedType));
396 return res;
397}
398
399void GlobalImportOp::print(OpAsmPrinter &printer) {
400 printer << " \"" << getImportName() << "\" from \"" << getModuleName()
401 << "\" as @" << getSymName();
402 if (getIsMutable())
403 printer << " mutable";
404 printer << " : " << getType();
405}
406
407//===----------------------------------------------------------------------===//
408// IfOp
409//===----------------------------------------------------------------------===//
410
411Block *IfOp::getLabelTarget() { return getTarget(); }
412
413//===----------------------------------------------------------------------===//
414// LocalOp
415//===----------------------------------------------------------------------===//
416
417LogicalResult LocalOp::inferReturnTypes(
418 MLIRContext *context, ::std::optional<Location> location,
419 ValueRange operands, DictionaryAttr attributes, PropertyRef properties,
420 RegionRange regions, SmallVectorImpl<Type> &inferredReturnTypes) {
421 LocalOp::GenericAdaptor<ValueRange> adaptor{operands, attributes, properties,
422 regions};
423 auto type = adaptor.getTypeAttr();
424 if (!type)
425 return failure();
426 auto resType = LocalRefType::get(type.getContext(), type.getValue());
427 inferredReturnTypes.push_back(resType);
428 return success();
429}
430
431//===----------------------------------------------------------------------===//
432// LocalGetOp
433//===----------------------------------------------------------------------===//
434
435LogicalResult LocalGetOp::inferReturnTypes(
436 MLIRContext *context, ::std::optional<Location> location,
437 ValueRange operands, DictionaryAttr attributes, PropertyRef properties,
438 RegionRange regions, SmallVectorImpl<Type> &inferredReturnTypes) {
439 return inferTeeGetResType(operands, inferredReturnTypes);
440}
441
442//===----------------------------------------------------------------------===//
443// LocalSetOp
444//===----------------------------------------------------------------------===//
445
446LogicalResult LocalSetOp::verify() {
447 if (getLocalVar().getType().getElementType() != getValue().getType())
448 return emitError("input type and result type of local.set do not match");
449 return success();
450}
451
452//===----------------------------------------------------------------------===//
453// LocalTeeOp
454//===----------------------------------------------------------------------===//
455
456LogicalResult LocalTeeOp::inferReturnTypes(
457 MLIRContext *context, ::std::optional<Location> location,
458 ValueRange operands, DictionaryAttr attributes, PropertyRef properties,
459 RegionRange regions, SmallVectorImpl<Type> &inferredReturnTypes) {
460 return inferTeeGetResType(operands, inferredReturnTypes);
461}
462
463LogicalResult LocalTeeOp::verify() {
464 if (getLocalVar().getType().getElementType() != getValue().getType() ||
465 getValue().getType() != getResult().getType())
466 return emitError("input type and output type of local.tee do not match");
467 return success();
468}
469
470//===----------------------------------------------------------------------===//
471// LoopOp
472//===----------------------------------------------------------------------===//
473
474Block *LoopOp::getLabelTarget() { return &getBody().front(); }
475
476//===----------------------------------------------------------------------===//
477// ReinterpretOp
478//===----------------------------------------------------------------------===//
479
480LogicalResult ReinterpretOp::verify() {
481 auto inT = getInput().getType();
482 auto resT = getResult().getType();
483 if (inT == resT)
484 return emitError("reinterpret input and output type should be distinct");
485 if (inT.getIntOrFloatBitWidth() != resT.getIntOrFloatBitWidth())
486 return emitError() << "input type (" << inT << ") and output type (" << resT
487 << ") have incompatible bit widths";
488 return success();
489}
490
491//===----------------------------------------------------------------------===//
492// ReturnOp
493//===----------------------------------------------------------------------===//
494
495void ReturnOp::build(OpBuilder &odsBuilder, OperationState &odsState) {}
return success()
b getContext())
static Type getElementType(Type type, ArrayRef< int32_t > indices, function_ref< InFlightDiagnostic(StringRef)> emitErrorFn)
Walks the given type hierarchy with the given indices, potentially down to component granularity,...
Definition SPIRVOps.cpp:229
ParseResult parseSymbolName(StringAttr &result)
Parse an -identifier and store it (without the '@' symbol) in a string attribute.
virtual ParseResult parseOptionalKeywordOrString(std::string *result)=0
Parse an optional keyword or string.
MLIRContext * getContext() const
virtual Location getEncodedSourceLoc(SMLoc loc)=0
Re-encode the given source location as an MLIR location and return it.
virtual InFlightDiagnostic emitError(SMLoc loc, const Twine &message={})=0
Emit a diagnostic at the specified location and return failure.
ParseResult parseKeywordOrString(std::string *result)
Parse a keyword or a quoted string.
ParseResult parseString(std::string *string)
Parse a quoted string token.
virtual SMLoc getCurrentLocation()=0
Get the location of the next token and store it into the argument.
virtual ParseResult parseColon()=0
Parse a : token.
virtual SMLoc getNameLoc() const =0
Return the location of the original name token.
virtual ParseResult parseType(Type &result)=0
Parse a type.
virtual void printKeywordOrString(StringRef keyword)
Print the given string as a keyword, or a quoted and escaped string if it has any special or non-prin...
Block represents an ordered list of Operations.
Definition Block.h:33
ValueTypeRange< BlockArgListType > getArgumentTypes()
Return a range containing the types of the arguments for this block.
Definition Block.cpp:154
unsigned getNumArguments()
Definition Block.h:152
Operation & front()
Definition Block.h:177
BlockArgument addArgument(Type type, Location loc)
Add one value to the argument list.
Definition Block.cpp:158
Block * getSuccessor(unsigned i)
Definition Block.cpp:274
This class is a general helper class for creating context-global objects like types,...
Definition Builders.h:51
FunctionType getFunctionType(TypeRange inputs, TypeRange results)
Definition Builders.cpp:84
MLIRContext is the top-level object for a collection of MLIR operations.
Definition MLIRContext.h:63
The OpAsmParser has methods for interacting with the asm parser: parsing things from it,...
virtual ParseResult parseRegion(Region &region, ArrayRef< Argument > arguments={}, bool enableNameShadowing=false)=0
Parses a region.
This is a pure-virtual base class that exposes the asmprinter hooks necessary to implement a custom p...
virtual void printRegion(Region &blocks, bool printEntryBlockArgs=true, bool printBlockTerminators=true, bool printEmptyBlock=false)=0
Prints a region.
This class helps build Operations.
Definition Builders.h:210
Operation is the basic unit of execution within MLIR.
Definition Operation.h:87
Type-safe wrapper around a void* for passing properties, including the properties structs of operatio...
This class provides an abstraction over the different types of ranges over Regions.
Definition Region.h:378
This class contains a list of basic blocks and a link to the parent operation it is attached to.
Definition Region.h:26
bool empty()
Definition Region.h:60
This class represents a collection of SymbolTables.
virtual Operation * lookupSymbolIn(Operation *symbolTableOp, StringAttr symbol)
Look up a symbol with the specified name within the specified symbol table operation,...
static Operation * getNearestSymbolTable(Operation *from)
Returns the nearest symbol table from a given operation from.
Instances of the Type class are uniqued, have an immutable identifier and an optional mutable compone...
Definition Types.h:74
This class provides an abstraction over the different types of ranges over Values.
Definition ValueRange.h:389
type_range getType() const
A named class for passing around the variadic flag.
void printFunctionOp(OpAsmPrinter &p, FunctionOpInterface op, bool isVariadic, StringRef typeAttrName, StringAttr argAttrsName, StringAttr resAttrsName)
Printer implementation for function-like operations.
ParseResult parseFunctionOp(OpAsmParser &parser, OperationState &result, bool allowVariadic, StringAttr typeAttrName, FuncTypeBuilder funcTypeBuilder, StringAttr argAttrsName, StringAttr resAttrsName)
Parser implementation for function-like operations.
Include the generated interface declarations.
Type getType(OpFoldResult ofr)
Returns the int type of the integer in ofr.
Definition Utils.cpp:310
InFlightDiagnostic emitError(Location loc)
Utility method to emit an error message using this location.
This represents an operation in an abstracted form, suitable for use with the builder APIs.