-
Notifications
You must be signed in to change notification settings - Fork 15.1k
[CIR] Add GlobalOp ctor and dtor regions #160779
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -683,8 +683,8 @@ def CIR_ConditionOp : CIR_Op<"condition", [ | |
| //===----------------------------------------------------------------------===// | ||
|
|
||
| defvar CIR_YieldableScopes = [ | ||
| "ArrayCtor", "ArrayDtor", "CaseOp", "DoWhileOp", "ForOp", "IfOp", "ScopeOp", | ||
| "SwitchOp", "TernaryOp", "WhileOp" | ||
| "ArrayCtor", "ArrayDtor", "CaseOp", "DoWhileOp", "ForOp", "GlobalOp", "IfOp", | ||
| "ScopeOp", "SwitchOp", "TernaryOp", "WhileOp" | ||
| ]; | ||
|
|
||
| def CIR_YieldOp : CIR_Op<"yield", [ | ||
|
|
@@ -1776,7 +1776,9 @@ def CIR_GlobalLinkageKind : CIR_I32EnumAttr< | |
| // is upstreamed. | ||
|
|
||
| def CIR_GlobalOp : CIR_Op<"global", [ | ||
| DeclareOpInterfaceMethods<CIRGlobalValueInterface> | ||
| DeclareOpInterfaceMethods<RegionBranchOpInterface>, | ||
| DeclareOpInterfaceMethods<CIRGlobalValueInterface>, | ||
| NoRegionArguments | ||
| ]> { | ||
| let summary = "Declare or define a global variable"; | ||
| let description = [{ | ||
|
|
@@ -1807,6 +1809,8 @@ def CIR_GlobalOp : CIR_Op<"global", [ | |
| UnitAttr:$dso_local, | ||
| OptionalAttr<I64Attr>:$alignment); | ||
|
|
||
| let regions = (region AnyRegion:$ctorRegion, AnyRegion:$dtorRegion); | ||
|
|
||
| let assemblyFormat = [{ | ||
| ($sym_visibility^)? | ||
| (`` $global_visibility^)? | ||
|
|
@@ -1815,24 +1819,34 @@ def CIR_GlobalOp : CIR_Op<"global", [ | |
| (`comdat` $comdat^)? | ||
| (`dso_local` $dso_local^)? | ||
| $sym_name | ||
| custom<GlobalOpTypeAndInitialValue>($sym_type, $initial_value) | ||
| custom<GlobalOpTypeAndInitialValue>($sym_type, $initial_value, | ||
| $ctorRegion, $dtorRegion) | ||
| attr-dict | ||
| }]; | ||
|
|
||
| let extraClassDeclaration = [{ | ||
| bool isDeclaration() { return !getInitialValue(); } | ||
| bool isDeclaration() { | ||
| return !getInitialValue() && getCtorRegion().empty() && getDtorRegion().empty(); | ||
| } | ||
| bool hasInitializer() { return !isDeclaration(); } | ||
| }]; | ||
|
|
||
| let skipDefaultBuilders = 1; | ||
|
|
||
| let builders = [OpBuilder<(ins | ||
| "llvm::StringRef":$sym_name, | ||
| "mlir::Type":$sym_type, | ||
| CArg<"bool", "false">:$isConstant, | ||
| // CIR defaults to external linkage. | ||
| CArg<"cir::GlobalLinkageKind", | ||
| "cir::GlobalLinkageKind::ExternalLinkage">:$linkage)>]; | ||
| let builders = [ | ||
| OpBuilder<(ins | ||
| "llvm::StringRef":$sym_name, | ||
| "mlir::Type":$sym_type, | ||
| CArg<"bool", "false">:$isConstant, | ||
| // CIR defaults to external linkage. | ||
| CArg<"cir::GlobalLinkageKind", | ||
| "cir::GlobalLinkageKind::ExternalLinkage">:$linkage, | ||
|
||
| CArg<"llvm::function_ref<void(mlir::OpBuilder &, mlir::Location)>", | ||
| "nullptr">:$ctorBuilder, | ||
| CArg<"llvm::function_ref<void(mlir::OpBuilder &, mlir::Location)>", | ||
| "nullptr">:$dtorBuilder) | ||
| > | ||
| ]; | ||
|
|
||
| let hasVerifier = 1; | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -1349,15 +1349,45 @@ mlir::LogicalResult cir::GlobalOp::verify() { | |
| return failure(); | ||
| } | ||
|
|
||
| // Verify that the constructor region, if present, has only one block which is | ||
| // not empty. | ||
| auto &ctorRegion = getCtorRegion(); | ||
| if (!ctorRegion.empty()) { | ||
| if (!ctorRegion.hasOneBlock()) { | ||
| return emitError() << "ctor region must have exactly one block."; | ||
| } | ||
|
||
|
|
||
| auto &block = ctorRegion.front(); | ||
| if (block.empty()) { | ||
| return emitError() << "ctor region shall not be empty."; | ||
| } | ||
| } | ||
|
|
||
| // Verify that the destructor region, if present, has only one block which is | ||
| // not empty. | ||
| auto &dtorRegion = getDtorRegion(); | ||
| if (!dtorRegion.empty()) { | ||
| if (!dtorRegion.hasOneBlock()) { | ||
| return emitError() << "dtor region must have exactly one block."; | ||
| } | ||
|
||
|
|
||
| auto &block = dtorRegion.front(); | ||
| if (block.empty()) { | ||
| return emitError() << "dtor region shall not be empty."; | ||
| } | ||
| } | ||
|
|
||
| // TODO(CIR): Many other checks for properties that haven't been upstreamed | ||
| // yet. | ||
| // yet (and some that have). | ||
|
|
||
| return success(); | ||
| } | ||
|
|
||
| void cir::GlobalOp::build(OpBuilder &odsBuilder, OperationState &odsState, | ||
| llvm::StringRef sym_name, mlir::Type sym_type, | ||
| bool isConstant, cir::GlobalLinkageKind linkage) { | ||
| void cir::GlobalOp::build( | ||
| OpBuilder &odsBuilder, OperationState &odsState, llvm::StringRef sym_name, | ||
| mlir::Type sym_type, bool isConstant, cir::GlobalLinkageKind linkage, | ||
| function_ref<void(OpBuilder &, Location)> ctorBuilder, | ||
| function_ref<void(OpBuilder &, Location)> dtorBuilder) { | ||
| odsState.addAttribute(getSymNameAttrName(odsState.name), | ||
| odsBuilder.getStringAttr(sym_name)); | ||
| odsState.addAttribute(getSymTypeAttrName(odsState.name), | ||
|
|
@@ -1370,43 +1400,139 @@ void cir::GlobalOp::build(OpBuilder &odsBuilder, OperationState &odsState, | |
| cir::GlobalLinkageKindAttr::get(odsBuilder.getContext(), linkage); | ||
| odsState.addAttribute(getLinkageAttrName(odsState.name), linkageAttr); | ||
|
|
||
| Region *ctorRegion = odsState.addRegion(); | ||
| if (ctorBuilder) { | ||
| odsBuilder.createBlock(ctorRegion); | ||
| ctorBuilder(odsBuilder, odsState.location); | ||
| } | ||
|
|
||
| Region *dtorRegion = odsState.addRegion(); | ||
| if (dtorBuilder) { | ||
| odsBuilder.createBlock(dtorRegion); | ||
| dtorBuilder(odsBuilder, odsState.location); | ||
| } | ||
|
|
||
| odsState.addAttribute(getGlobalVisibilityAttrName(odsState.name), | ||
| cir::VisibilityAttr::get(odsBuilder.getContext())); | ||
| } | ||
|
|
||
| /// Given the region at `index`, or the parent operation if `index` is None, | ||
| /// return the successor regions. These are the regions that may be selected | ||
| /// during the flow of control. `operands` is a set of optional attributes that | ||
| /// correspond to a constant value for each operand, or null if that operand is | ||
| /// not a constant. | ||
| void cir::GlobalOp::getSuccessorRegions( | ||
| mlir::RegionBranchPoint point, SmallVectorImpl<RegionSuccessor> ®ions) { | ||
| // The `ctor` and `dtor` regions always branch back to the parent operation. | ||
| if (!point.isParent()) { | ||
| regions.push_back(RegionSuccessor()); | ||
| return; | ||
| } | ||
|
|
||
| // Don't consider the ctor region if it is empty. | ||
| Region *ctorRegion = &this->getCtorRegion(); | ||
| if (ctorRegion->empty()) | ||
| ctorRegion = nullptr; | ||
|
|
||
| // Don't consider the dtor region if it is empty. | ||
| Region *dtorRegion = &this->getCtorRegion(); | ||
| if (dtorRegion->empty()) | ||
| dtorRegion = nullptr; | ||
|
|
||
| // If the condition isn't constant, both regions may be executed. | ||
| if (ctorRegion) | ||
| regions.push_back(RegionSuccessor(ctorRegion)); | ||
| if (dtorRegion) | ||
| regions.push_back(RegionSuccessor(dtorRegion)); | ||
| } | ||
|
|
||
| static void printGlobalOpTypeAndInitialValue(OpAsmPrinter &p, cir::GlobalOp op, | ||
| TypeAttr type, | ||
| Attribute initAttr) { | ||
| TypeAttr type, Attribute initAttr, | ||
| mlir::Region &ctorRegion, | ||
| mlir::Region &dtorRegion) { | ||
| auto printType = [&]() { p << ": " << type; }; | ||
| if (!op.isDeclaration()) { | ||
| p << "= "; | ||
| // This also prints the type... | ||
| if (initAttr) | ||
| printConstant(p, initAttr); | ||
| if (!ctorRegion.empty()) { | ||
| p << "ctor "; | ||
| printType(); | ||
| p << " "; | ||
| p.printRegion(ctorRegion, | ||
| /*printEntryBlockArgs=*/false, | ||
| /*printBlockTerminators=*/false); | ||
| } else { | ||
| // This also prints the type... | ||
| if (initAttr) | ||
| printConstant(p, initAttr); | ||
| } | ||
|
|
||
| if (!dtorRegion.empty()) { | ||
| p << " dtor "; | ||
| p.printRegion(dtorRegion, | ||
| /*printEntryBlockArgs=*/false, | ||
| /*printBlockTerminators=*/false); | ||
| } | ||
| } else { | ||
| p << ": " << type; | ||
| printType(); | ||
| } | ||
| } | ||
|
|
||
| static ParseResult | ||
| parseGlobalOpTypeAndInitialValue(OpAsmParser &parser, TypeAttr &typeAttr, | ||
| Attribute &initialValueAttr) { | ||
| static ParseResult parseGlobalOpTypeAndInitialValue(OpAsmParser &parser, | ||
| TypeAttr &typeAttr, | ||
| Attribute &initialValueAttr, | ||
| mlir::Region &ctorRegion, | ||
| mlir::Region &dtorRegion) { | ||
| mlir::Type opTy; | ||
| if (parser.parseOptionalEqual().failed()) { | ||
| // Absence of equal means a declaration, so we need to parse the type. | ||
| // cir.global @a : !cir.int<s, 32> | ||
| if (parser.parseColonType(opTy)) | ||
| return failure(); | ||
| } else { | ||
| // Parse constant with initializer, examples: | ||
| // cir.global @y = #cir.fp<1.250000e+00> : !cir.double | ||
| // cir.global @rgb = #cir.const_array<[...] : !cir.array<i8 x 3>> | ||
| if (parseConstantValue(parser, initialValueAttr).failed()) | ||
| return failure(); | ||
| // Parse contructor, example: | ||
| // cir.global @rgb = ctor : type { ... } | ||
| if (!parser.parseOptionalKeyword("ctor")) { | ||
| if (parser.parseColonType(opTy)) | ||
| return failure(); | ||
| auto parseLoc = parser.getCurrentLocation(); | ||
| if (parser.parseRegion(ctorRegion, /*arguments=*/{}, /*argTypes=*/{})) | ||
| return failure(); | ||
| if (!ctorRegion.hasOneBlock()) | ||
| return parser.emitError(parser.getCurrentLocation(), | ||
| "ctor region must have exactly one block"); | ||
| if (ctorRegion.back().empty()) | ||
| return parser.emitError(parser.getCurrentLocation(), | ||
| "ctor region shall not be empty"); | ||
| if (ensureRegionTerm(parser, ctorRegion, parseLoc).failed()) | ||
| return failure(); | ||
| } else { | ||
| // Parse constant with initializer, examples: | ||
| // cir.global @y = 3.400000e+00 : f32 | ||
| // cir.global @rgb = #cir.const_array<[...] : !cir.array<i8 x 3>> | ||
| if (parseConstantValue(parser, initialValueAttr).failed()) | ||
| return failure(); | ||
|
|
||
| assert(mlir::isa<mlir::TypedAttr>(initialValueAttr) && | ||
| "Non-typed attrs shouldn't appear here."); | ||
| auto typedAttr = mlir::cast<mlir::TypedAttr>(initialValueAttr); | ||
| opTy = typedAttr.getType(); | ||
| assert(mlir::isa<mlir::TypedAttr>(initialValueAttr) && | ||
| "Non-typed attrs shouldn't appear here."); | ||
| auto typedAttr = mlir::cast<mlir::TypedAttr>(initialValueAttr); | ||
| opTy = typedAttr.getType(); | ||
| } | ||
|
|
||
| // Parse destructor, example: | ||
| // dtor { ... } | ||
| if (!parser.parseOptionalKeyword("dtor")) { | ||
| auto parseLoc = parser.getCurrentLocation(); | ||
| if (parser.parseRegion(dtorRegion, /*arguments=*/{}, /*argTypes=*/{})) | ||
| return failure(); | ||
| if (!dtorRegion.hasOneBlock()) | ||
| return parser.emitError(parser.getCurrentLocation(), | ||
| "dtor region must have exactly one block"); | ||
| if (dtorRegion.back().empty()) | ||
| return parser.emitError(parser.getCurrentLocation(), | ||
| "dtor region shall not be empty"); | ||
| if (ensureRegionTerm(parser, dtorRegion, parseLoc).failed()) | ||
| return failure(); | ||
| } | ||
| } | ||
|
|
||
| typeAttr = TypeAttr::get(opTy); | ||
|
|
||
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -0,0 +1,48 @@ | ||||||
| // RUN: cir-opt %s -o - | FileCheck %s | ||||||
|
||||||
| // RUN: cir-opt %s -o - | FileCheck %s | |
| // RUN: cir-opt --verify-roundtrip %s -o - | FileCheck %s |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.