diff --git a/include/vast/CodeGen/DefaultStmtVisitor.hpp b/include/vast/CodeGen/DefaultStmtVisitor.hpp index fc57b4411f..24f635d1a1 100644 --- a/include/vast/CodeGen/DefaultStmtVisitor.hpp +++ b/include/vast/CodeGen/DefaultStmtVisitor.hpp @@ -196,6 +196,7 @@ namespace vast::cg { operation VisitMemberExpr(const clang::MemberExpr *expr); operation VisitConditionalOperator(const clang::ConditionalOperator *op); + operation VisitChooseExpr(const clang::ChooseExpr *expr); operation VisitBinaryConditionalOperator(const clang::BinaryConditionalOperator *op); operation VisitAddrLabelExpr(const clang::AddrLabelExpr *expr); operation VisitConstantExpr(const clang::ConstantExpr *expr); diff --git a/include/vast/Dialect/HighLevel/HighLevelCF.td b/include/vast/Dialect/HighLevel/HighLevelCF.td index 9f3bb54065..8b79c65131 100644 --- a/include/vast/Dialect/HighLevel/HighLevelCF.td +++ b/include/vast/Dialect/HighLevel/HighLevelCF.td @@ -20,7 +20,7 @@ class HighLevel_ControlFlowOp< string mnemonic, list< Trait > traits = [] > def HighLevel_CondYieldOp : HighLevel_Op< "cond.yield", [ // TODO(Heno): add ReturnLike trait - Terminator, ParentOneOf<["IfOp", "WhileOp", "ForOp", "DoOp", "CondOp", "BinaryCondOp"]> + Terminator, ParentOneOf<["IfOp", "WhileOp", "ForOp", "DoOp", "CondOp", "ChooseExprOp", "BinaryCondOp"]> ] > { let summary = "condition yield operation"; @@ -130,11 +130,11 @@ def HighLevel_IfOp let hasCustomAssemblyFormat = 1; } -def HighLevel_CondOp - : HighLevel_ControlFlowOp< "cond" > +class HighLevel_CondOpBase< string mnemonic, list< Trait > traits = [] > + + : HighLevel_ControlFlowOp< mnemonic, traits > , Results<(outs AnyType:$result)> { - let summary = "VAST conditional statement"; let description = [{ The operation takes builders of three regions -- condition, true branch and false branch. Builders, given the location, build a particular region. @@ -166,12 +166,91 @@ def HighLevel_CondOp "builder_callback_ref":$condBuilder, "builder_callback_ref":$thenBuilder, "builder_callback_ref":$elseBuilder - )> + ), [{ + InsertionGuard guard($_builder); + build_region($_builder, $_state, condBuilder); + build_region($_builder, $_state, thenBuilder); + build_region($_builder, $_state, elseBuilder); + $_state.addTypes(type); + }] > ]; let assemblyFormat = [{ attr-dict `:` type(results) $condRegion `?` $thenRegion `:` $elseRegion }]; } +def HighLevel_CondOp : HighLevel_CondOpBase< "cond" > { + let summary = "VAST conditional statement"; +} + +def HighLevel_ChooseExprOp : HighLevel_ControlFlowOp< "choose_expr" > + , Arguments<(ins OptionalAttr< BoolAttr >:$condTrue)> + , Results<(outs AnyType:$result)> +{ + let summary = "Representation of GNU __builtin_choose_expr"; + + let description = [{ + The operation takes builders of three regions -- condition, true branch and false branch. + Builders, given the location, build a particular region. + + The generic form of the operation is as follows: + + hl.cond { + ... /* condition region */ + hl.cond.yield %cond : !hl.bool + } ? { + ... /* true region */ + } : { + ... /* false region */ + } + }]; + + let regions = (region + Core_CondRegion:$condRegion, + Core_ValueRegion:$thenRegion, + Core_ValueRegion:$elseRegion + ); + + let skipDefaultBuilders = 1; + let builders = [ + OpBuilder<(ins + "Type":$type, + "builder_callback_ref":$condBuilder, + "builder_callback_ref":$thenBuilder, + "builder_callback_ref":$elseBuilder, + CArg<"std::optional< bool >", "std::nullopt">:$condTrue + ), [{ + InsertionGuard guard($_builder); + build_region($_builder, $_state, condBuilder); + build_region($_builder, $_state, thenBuilder); + build_region($_builder, $_state, elseBuilder); + if (condTrue) { + $_state.addAttribute("condTrue", $_builder.getBoolAttr(condTrue.value())); + } + $_state.addTypes(type); + }] > + ]; + + let extraClassDeclaration = [{ + std::optional< bool > isConditionTrue() { + if (auto cond = getCondTrue()) { + return cond.value(); + } + return {}; + } + + bool isConditionDependent() { return (bool) getCondTrueAttr(); } + + region_ptr getChosenSubExpr() { + if (auto cond = isConditionTrue()) { + return cond.value() ? &getThenRegion() : &getElseRegion(); + } + return {}; + } + }]; + + let assemblyFormat = [{ attr-dict (`cond` $condTrue^)? `:` type(results) $condRegion `?` $thenRegion `:` $elseRegion }]; +} + def HighLevel_BinaryCondOp : HighLevel_Op< "binary_cond", diff --git a/lib/vast/CodeGen/DefaultStmtVisitor.cpp b/lib/vast/CodeGen/DefaultStmtVisitor.cpp index 9b937d7783..7094e531ff 100644 --- a/lib/vast/CodeGen/DefaultStmtVisitor.cpp +++ b/lib/vast/CodeGen/DefaultStmtVisitor.cpp @@ -850,6 +850,18 @@ namespace vast::cg .freeze(); } + operation default_stmt_visitor::VisitChooseExpr(const clang::ChooseExpr *expr) { + return bld.compose< hl::ChooseExprOp >() + .bind(self.location(expr)) + // ChooseExpr conserves everything including lvalue-ness + .bind(visit_maybe_lvalue_result_type(expr)) + .bind_always(mk_cond_builder(expr->getCond())) + .bind_always(mk_value_builder(expr->getLHS())) + .bind_always(mk_value_builder(expr->getRHS())) + .bind_always(expr->isConditionDependent() ? std::nullopt : std::optional(expr->isConditionTrue())) + .freeze(); + } + operation default_stmt_visitor::VisitBinaryConditionalOperator(const clang::BinaryConditionalOperator *op) { auto common_type = self.visit(op->getCommon()->getType()); return bld.compose< hl::BinaryCondOp >() diff --git a/lib/vast/Dialect/HighLevel/HighLevelOps.cpp b/lib/vast/Dialect/HighLevel/HighLevelOps.cpp index 6dfbaf48b4..29249df4f1 100644 --- a/lib/vast/Dialect/HighLevel/HighLevelOps.cpp +++ b/lib/vast/Dialect/HighLevel/HighLevelOps.cpp @@ -569,18 +569,6 @@ namespace vast::hl build_region(bld, st, else_builder); } - void CondOp::build( - Builder &bld, State &st, Type type, - builder_callback_ref cond, - builder_callback_ref then_builder, - builder_callback_ref else_builder - ) { - InsertionGuard guard(bld); - build_region(bld, st, cond); - build_region(bld, st, then_builder); - build_region(bld, st, else_builder); - st.addTypes(type); - } void BinaryCondOp::build( Builder &bld, State &st, Type type, builder_callback_ref common_builder, @@ -827,6 +815,7 @@ namespace vast::hl GRAPH_REGION_OP(LabelStmt); GRAPH_REGION_OP(BreakOp); GRAPH_REGION_OP(CondOp); + GRAPH_REGION_OP(ChooseExprOp); GRAPH_REGION_OP(BinaryCondOp); GRAPH_REGION_OP(ContinueOp); diff --git a/test/vast/Dialect/HighLevel/choose-expr-a.c b/test/vast/Dialect/HighLevel/choose-expr-a.c new file mode 100644 index 0000000000..0f3c36ebe3 --- /dev/null +++ b/test/vast/Dialect/HighLevel/choose-expr-a.c @@ -0,0 +1,27 @@ +// RUN: %vast-cc1 -vast-emit-mlir=hl %s -o - | %file-check %s +// RUN: %vast-cc1 -vast-emit-mlir=hl %s -o %t && %vast-opt %t | diff -B %t - + +void fn() { + int x, y; + // CHECK: hl.choose_expr cond false : !hl.lvalue { + // CHECK: hl.cond.yield {{%.*}} : !hl.int + // CHECK: hl.value.yield {{%.*}} : !hl.lvalue + // CHECK: hl.value.yield {{%.*}} : !hl.lvalue + int z = __builtin_choose_expr(0, x, y); + // CHECK: hl.choose_expr cond true : !hl.lvalue { + z = __builtin_choose_expr(1, x, y); + + // CHECK: hl.choose_expr cond true : !hl.lvalue { + // CHECK: hl.value.yield {{%.*}} : !hl.lvalue + // CHECK: hl.value.yield {{%.*}} : !hl.void + z = __builtin_choose_expr(1, x, (void) 0); + // CHECK: hl.choose_expr cond false : !hl.lvalue { + // CHECK: hl.value.yield {{%.*}} : !hl.void + // CHECK: hl.value.yield {{%.*}} : !hl.lvalue + z = __builtin_choose_expr(0, (void) 0, x); + + // CHECK: hl.choose_expr cond true : !hl.lvalue { + // CHECK: hl.value.yield {{%.*}} : !hl.lvalue + // CHECK: hl.value.yield {{%.*}} : !hl.char + z = __builtin_choose_expr(1, x, (char) 0); +}