MLIR 23.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, SymbolTable::getSymbolAttrName(),
91 result.attributes);
92 return res;
93}
94} // namespace
95
96//===----------------------------------------------------------------------===//
97// BlockOp
98//===----------------------------------------------------------------------===//
99
100Block *BlockOp::getLabelTarget() { return getTarget(); }
101
102//===----------------------------------------------------------------------===//
103// BlockReturnOp
104//===----------------------------------------------------------------------===//
105
106std::size_t BlockReturnOp::getExitLevel() { return 0; }
107
108Block *BlockReturnOp::getTarget() {
109 return cast<LabelBranchingOpInterface>(getOperation())
110 .getTargetOp()
111 .getOperation()
112 ->getSuccessor(0);
113}
114
115//===----------------------------------------------------------------------===//
116// ExtendLowBitsSOp
117//===----------------------------------------------------------------------===//
118
119LogicalResult ExtendLowBitsSOp::verify() {
120 auto bitsToTake = getBitsToTake().getValue().getLimitedValue();
121 if (bitsToTake != 32 && bitsToTake != 16 && bitsToTake != 8)
122 return emitError("extend op can only take 8, 16 or 32 bits. Got ")
123 << bitsToTake;
124
125 if (bitsToTake >= getInput().getType().getIntOrFloatBitWidth())
126 return emitError("trying to extend the ")
127 << bitsToTake << " low bits from a " << getInput().getType()
128 << " value is illegal";
129 return success();
130}
131
132//===----------------------------------------------------------------------===//
133// FuncOp
134//===----------------------------------------------------------------------===//
135
136Block *FuncOp::addEntryBlock() {
137 if (!getBody().empty()) {
138 emitError("adding entry block to a FuncOp which already has one");
139 return &getBody().front();
140 }
141 Block &block = getBody().emplaceBlock();
142 for (auto argType : getFunctionType().getInputs())
143 block.addArgument(LocalRefType::get(argType), getLoc());
144 return &block;
145}
146
147void FuncOp::build(OpBuilder &odsBuilder, OperationState &odsState,
148 StringRef symbol, FunctionType funcType) {
149 FuncOp::build(odsBuilder, odsState, symbol, funcType, {}, {});
150}
151
152ParseResult FuncOp::parse(OpAsmParser &parser, OperationState &result) {
153 auto *ctx = parser.getContext();
154 std::string visibilityString;
155 auto loc = parser.getNameLoc();
156 ParseResult res = parser.parseOptionalKeywordOrString(&visibilityString);
157 bool exported{false};
158 if (res.succeeded()) {
159 if (visibilityString != "exported")
160 return parser.emitError(
161 loc, "expecting either `exported` or symbol name. got ")
162 << visibilityString;
163 exported = true;
164 }
165
166 auto buildFuncType = [&parser](Builder &builder, ArrayRef<Type> argTypes,
167 ArrayRef<Type> results,
169 std::string &) {
170 SmallVector<Type> argTypesWithoutLocal{};
171 argTypesWithoutLocal.reserve(argTypes.size());
172 llvm::for_each(argTypes, [&parser, &argTypesWithoutLocal](Type argType) {
173 auto refType = dyn_cast<LocalRefType>(argType);
174 auto loc = parser.getEncodedSourceLoc(parser.getCurrentLocation());
175 if (!refType) {
176 mlir::emitError(loc, "invalid type for wasm.func argument. Expecting "
177 "!wasm<local T>, got ")
178 << argType;
179 return;
180 }
181 argTypesWithoutLocal.push_back(refType.getElementType());
182 });
183
184 return builder.getFunctionType(argTypesWithoutLocal, results);
185 };
187 parser, result, /*allowVariadic=*/false,
188 getFunctionTypeAttrName(result.name), buildFuncType,
189 getArgAttrsAttrName(result.name), getResAttrsAttrName(result.name));
190 if (exported)
191 result.addAttribute(getExportedAttrName(result.name), UnitAttr::get(ctx));
192 return funcParseRes;
193}
194
195LogicalResult FuncOp::verifyBody() {
196 if (getBody().empty())
197 return success();
198 Block &entry = getBody().front();
199 if (entry.getNumArguments() != getFunctionType().getNumInputs())
200 return emitError("entry block should have same number of arguments as "
201 "function type. Function type has ")
202 << getFunctionType().getNumInputs() << ", entry block has "
203 << entry.getNumArguments();
204
205 for (auto [argNo, funcSignatureType, blockType] : llvm::enumerate(
206 getFunctionType().getInputs(), entry.getArgumentTypes())) {
207 auto blockLocalRefType = dyn_cast<LocalRefType>(blockType);
208 if (!blockLocalRefType)
209 return emitError("entry block argument type should be LocalRefType, got ")
210 << blockType << " for block argument " << argNo;
211 if (blockLocalRefType.getElementType() != funcSignatureType)
212 return emitError("func argument type #")
213 << argNo << "(" << funcSignatureType
214 << ") doesn't match entry block referenced type ("
215 << blockLocalRefType.getElementType() << ")";
216 }
217 return success();
218}
219
220void FuncOp::print(OpAsmPrinter &p) {
221 /// If exported, print it before and mask it before printing
222 /// using generic interface.
223 auto exported = getExported();
224 if (exported) {
225 p << " exported";
226 removeExportedAttr();
227 }
229 p, *this, /*isVariadic=*/false, getFunctionTypeAttrName(),
230 getArgAttrsAttrName(), getResAttrsAttrName());
231 if (exported)
232 setExported(true);
233}
234
235//===----------------------------------------------------------------------===//
236// FuncImportOp
237//===----------------------------------------------------------------------===//
238
239void FuncImportOp::build(OpBuilder &odsBuilder, OperationState &odsState,
240 StringRef symbol, StringRef moduleName,
241 StringRef importName, FunctionType type) {
242 FuncImportOp::build(odsBuilder, odsState, symbol, moduleName, importName,
243 type, {}, {});
244}
245
246//===----------------------------------------------------------------------===//
247// GlobalOp
248//===----------------------------------------------------------------------===//
249namespace {
250Operation *getGlobalOpTerminatorOp(GlobalOp gop) {
251 return gop.getInitializer().begin()->getTerminator();
252}
253} // namespace
254
255ReturnOp GlobalOp::getInitTerminator() {
256 return llvm::cast<wasmssa::ReturnOp>(getGlobalOpTerminatorOp(*this));
257}
258
259// Custom formats
260ParseResult GlobalOp::parse(OpAsmParser &parser, OperationState &result) {
261 StringAttr symbolName;
262 Type globalType;
263 auto *ctx = parser.getContext();
264 std::string visibilityString;
265 auto loc = parser.getNameLoc();
266 ParseResult res = parser.parseOptionalKeywordOrString(&visibilityString);
267 if (res.succeeded()) {
268 if (visibilityString != "exported")
269 return parser.emitError(
270 loc, "expecting either `exported` or symbol name. got ")
271 << visibilityString;
272 result.addAttribute(getExportedAttrName(result.name), UnitAttr::get(ctx));
273 }
274
275 res = parser.parseSymbolName(symbolName, SymbolTable::getSymbolAttrName(),
276 result.attributes);
277 res = parser.parseType(globalType);
278 result.addAttribute(getTypeAttrName(result.name), TypeAttr::get(globalType));
279 std::string mutableString;
280 res = parser.parseOptionalKeywordOrString(&mutableString);
281 if (res.succeeded() && mutableString == "mutable")
282 result.addAttribute("isMutable", UnitAttr::get(ctx));
283
284 res = parser.parseColon();
285 Region *globalInitRegion = result.addRegion();
286 res = parser.parseRegion(*globalInitRegion);
287 return res;
288}
289
290void GlobalOp::print(OpAsmPrinter &printer) {
291 if (getExported())
292 printer << " exported";
293 printer << " @" << getSymName().str() << " " << getType();
294 if (getIsMutable())
295 printer << " mutable";
296 printer << " :";
297 Region &body = getRegion();
298 if (!body.empty()) {
299 printer << ' ';
300 printer.printRegion(body, /*printEntryBlockArgs=*/false,
301 /*printBlockTerminators=*/true);
302 }
303}
304
305LogicalResult GlobalOp::verify() {
306 return success(llvm::isa<ReturnOp>(getGlobalOpTerminatorOp(*this)));
307}
308
309//===----------------------------------------------------------------------===//
310// GlobalGetOp
311//===----------------------------------------------------------------------===//
312
313LogicalResult
314GlobalGetOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
315 // If the parent requires a constant context, verify that global.get is a
316 // constant as defined per the wasm standard.
317 if (!this->getOperation()
318 ->getParentWithTrait<ConstantExpressionInitializerOpTrait>())
319 return success();
321 StringRef referencedSymbol = getGlobal();
322 Operation *definitionOp = symbolTable.lookupSymbolIn(
323 symTabOp, StringAttr::get(this->getContext(), referencedSymbol));
324 if (!definitionOp)
325 return emitError() << "symbol @" << referencedSymbol << " is undefined";
326 auto definitionImport = dyn_cast<GlobalImportOp>(definitionOp);
327 if (!definitionImport || definitionImport.getIsMutable()) {
328 return emitError("global.get op is considered constant if it's referring "
329 "to a import.global symbol marked non-mutable");
330 }
331 return success();
332}
333
334//===----------------------------------------------------------------------===//
335// GlobalImportOp
336//===----------------------------------------------------------------------===//
337
338ParseResult GlobalImportOp::parse(OpAsmParser &parser, OperationState &result) {
339 auto *ctx = parser.getContext();
340 ParseResult res = parseImportOp(parser, result);
341 if (res.failed())
342 return failure();
343 std::string mutableOrSymVisString;
344 res = parser.parseOptionalKeywordOrString(&mutableOrSymVisString);
345 if (res.succeeded() && mutableOrSymVisString == "mutable") {
346 result.addAttribute("isMutable", UnitAttr::get(ctx));
347 }
348
349 res = parser.parseColon();
350
351 Type importedType;
352 res = parser.parseType(importedType);
353 if (res.succeeded())
354 result.addAttribute(getTypeAttrName(result.name),
355 TypeAttr::get(importedType));
356 return res;
357}
358
359void GlobalImportOp::print(OpAsmPrinter &printer) {
360 printer << " \"" << getImportName() << "\" from \"" << getModuleName()
361 << "\" as @" << getSymName();
362 if (getIsMutable())
363 printer << " mutable";
364 printer << " : " << getType();
365}
366
367//===----------------------------------------------------------------------===//
368// IfOp
369//===----------------------------------------------------------------------===//
370
371Block *IfOp::getLabelTarget() { return getTarget(); }
372
373//===----------------------------------------------------------------------===//
374// LocalOp
375//===----------------------------------------------------------------------===//
376
377LogicalResult LocalOp::inferReturnTypes(
378 MLIRContext *context, ::std::optional<Location> location,
379 ValueRange operands, DictionaryAttr attributes, PropertyRef properties,
380 RegionRange regions, SmallVectorImpl<Type> &inferredReturnTypes) {
381 LocalOp::GenericAdaptor<ValueRange> adaptor{operands, attributes, properties,
382 regions};
383 auto type = adaptor.getTypeAttr();
384 if (!type)
385 return failure();
386 auto resType = LocalRefType::get(type.getContext(), type.getValue());
387 inferredReturnTypes.push_back(resType);
388 return success();
389}
390
391//===----------------------------------------------------------------------===//
392// LocalGetOp
393//===----------------------------------------------------------------------===//
394
395LogicalResult LocalGetOp::inferReturnTypes(
396 MLIRContext *context, ::std::optional<Location> location,
397 ValueRange operands, DictionaryAttr attributes, PropertyRef properties,
398 RegionRange regions, SmallVectorImpl<Type> &inferredReturnTypes) {
399 return inferTeeGetResType(operands, inferredReturnTypes);
400}
401
402//===----------------------------------------------------------------------===//
403// LocalSetOp
404//===----------------------------------------------------------------------===//
405
406LogicalResult LocalSetOp::verify() {
407 if (getLocalVar().getType().getElementType() != getValue().getType())
408 return emitError("input type and result type of local.set do not match");
409 return success();
410}
411
412//===----------------------------------------------------------------------===//
413// LocalTeeOp
414//===----------------------------------------------------------------------===//
415
416LogicalResult LocalTeeOp::inferReturnTypes(
417 MLIRContext *context, ::std::optional<Location> location,
418 ValueRange operands, DictionaryAttr attributes, PropertyRef properties,
419 RegionRange regions, SmallVectorImpl<Type> &inferredReturnTypes) {
420 return inferTeeGetResType(operands, inferredReturnTypes);
421}
422
423LogicalResult LocalTeeOp::verify() {
424 if (getLocalVar().getType().getElementType() != getValue().getType() ||
425 getValue().getType() != getResult().getType())
426 return emitError("input type and output type of local.tee do not match");
427 return success();
428}
429
430//===----------------------------------------------------------------------===//
431// LoopOp
432//===----------------------------------------------------------------------===//
433
434Block *LoopOp::getLabelTarget() { return &getBody().front(); }
435
436//===----------------------------------------------------------------------===//
437// ReinterpretOp
438//===----------------------------------------------------------------------===//
439
440LogicalResult ReinterpretOp::verify() {
441 auto inT = getInput().getType();
442 auto resT = getResult().getType();
443 if (inT == resT)
444 return emitError("reinterpret input and output type should be distinct");
445 if (inT.getIntOrFloatBitWidth() != resT.getIntOrFloatBitWidth())
446 return emitError() << "input type (" << inT << ") and output type (" << resT
447 << ") have incompatible bit widths";
448 return success();
449}
450
451//===----------------------------------------------------------------------===//
452// ReturnOp
453//===----------------------------------------------------------------------===//
454
455void ReturnOp::build(OpBuilder &odsBuilder, OperationState &odsState) {}
return success()
static Type getElementType(Type type)
Determine the element type of type.
b getContext())
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:138
Operation & front()
Definition Block.h:163
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:80
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:209
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:357
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 StringRef getSymbolAttrName()
Return the name of the attribute used for symbol names.
Definition SymbolTable.h:76
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:307
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.