diff options
Diffstat (limited to 'frontends')
-rw-r--r-- | frontends/aiger/aigerparse.cc | 14 | ||||
-rw-r--r-- | frontends/aiger/aigerparse.h | 2 | ||||
-rw-r--r-- | frontends/ast/ast.cc | 6 | ||||
-rw-r--r-- | frontends/ast/ast.h | 3 | ||||
-rw-r--r-- | frontends/ast/genrtlil.cc | 60 | ||||
-rw-r--r-- | frontends/ast/simplify.cc | 113 | ||||
-rw-r--r-- | frontends/ilang/ilang_lexer.l | 4 | ||||
-rw-r--r-- | frontends/ilang/ilang_parser.y | 6 | ||||
-rw-r--r-- | frontends/verilog/verilog_frontend.cc | 2 | ||||
-rw-r--r-- | frontends/verilog/verilog_frontend.h | 2 | ||||
-rw-r--r-- | frontends/verilog/verilog_lexer.l | 15 | ||||
-rw-r--r-- | frontends/verilog/verilog_parser.y | 156 |
12 files changed, 254 insertions, 129 deletions
diff --git a/frontends/aiger/aigerparse.cc b/frontends/aiger/aigerparse.cc index 6fda92d73..d25587e48 100644 --- a/frontends/aiger/aigerparse.cc +++ b/frontends/aiger/aigerparse.cc @@ -454,6 +454,14 @@ void AigerReader::parse_xaiger() for (unsigned i = 0; i < flopNum; i++) mergeability.emplace_back(parse_xaiger_literal(f)); } + else if (c == 's') { + uint32_t dataSize YS_ATTRIBUTE(unused) = parse_xaiger_literal(f); + flopNum = parse_xaiger_literal(f); + log_assert(dataSize == (flopNum+1) * sizeof(uint32_t)); + initial_state.reserve(flopNum); + for (unsigned i = 0; i < flopNum; i++) + initial_state.emplace_back(parse_xaiger_literal(f)); + } else if (c == 'n') { parse_xaiger_literal(f); f >> s; @@ -767,6 +775,7 @@ void AigerReader::post_process() } } + dict<int, Wire*> mergeability_to_clock; for (uint32_t i = 0; i < flopNum; i++) { RTLIL::Wire *d = outputs[outputs.size() - flopNum + i]; log_assert(d); @@ -778,10 +787,9 @@ void AigerReader::post_process() log_assert(q->port_input); q->port_input = false; - auto ff = module->addCell(NEW_ID, ID($__ABC9_FF_)); - ff->setPort(ID::D, d); - ff->setPort(ID::Q, q); + Cell* ff = module->addFfGate(NEW_ID, d, q); ff->attributes[ID::abc9_mergeability] = mergeability[i]; + q->attributes[ID::init] = initial_state[i]; } dict<RTLIL::IdString, std::pair<int,int>> wideports_cache; diff --git a/frontends/aiger/aigerparse.h b/frontends/aiger/aigerparse.h index 46ac81212..251a24977 100644 --- a/frontends/aiger/aigerparse.h +++ b/frontends/aiger/aigerparse.h @@ -45,7 +45,7 @@ struct AigerReader std::vector<RTLIL::Wire*> outputs; std::vector<RTLIL::Wire*> bad_properties; std::vector<RTLIL::Cell*> boxes; - std::vector<int> mergeability; + std::vector<int> mergeability, initial_state; AigerReader(RTLIL::Design *design, std::istream &f, RTLIL::IdString module_name, RTLIL::IdString clk_name, std::string map_filename, bool wideports); void parse_aiger(); diff --git a/frontends/ast/ast.cc b/frontends/ast/ast.cc index 6a9af3f57..689fa9fb4 100644 --- a/frontends/ast/ast.cc +++ b/frontends/ast/ast.cc @@ -94,6 +94,7 @@ std::string AST::type2str(AstNodeType type) X(AST_TO_BITS) X(AST_TO_SIGNED) X(AST_TO_UNSIGNED) + X(AST_SELFSZ) X(AST_CONCAT) X(AST_REPLICATE) X(AST_BIT_NOT) @@ -110,6 +111,8 @@ std::string AST::type2str(AstNodeType type) X(AST_SHIFT_RIGHT) X(AST_SHIFT_SLEFT) X(AST_SHIFT_SRIGHT) + X(AST_SHIFTX) + X(AST_SHIFT) X(AST_LT) X(AST_LE) X(AST_EQ) @@ -615,6 +618,7 @@ void AstNode::dumpVlog(FILE *f, std::string indent) const if (0) { case AST_POS: txt = "+"; } if (0) { case AST_NEG: txt = "-"; } if (0) { case AST_LOGIC_NOT: txt = "!"; } + if (0) { case AST_SELFSZ: txt = "@selfsz@"; } fprintf(f, "%s(", txt.c_str()); children[0]->dumpVlog(f, ""); fprintf(f, ")"); @@ -628,6 +632,8 @@ void AstNode::dumpVlog(FILE *f, std::string indent) const if (0) { case AST_SHIFT_RIGHT: txt = ">>"; } if (0) { case AST_SHIFT_SLEFT: txt = "<<<"; } if (0) { case AST_SHIFT_SRIGHT: txt = ">>>"; } + if (0) { case AST_SHIFTX: txt = "@shiftx@"; } + if (0) { case AST_SHIFT: txt = "@shift@"; } if (0) { case AST_LT: txt = "<"; } if (0) { case AST_LE: txt = "<="; } if (0) { case AST_EQ: txt = "=="; } diff --git a/frontends/ast/ast.h b/frontends/ast/ast.h index 3f6329112..8932108e3 100644 --- a/frontends/ast/ast.h +++ b/frontends/ast/ast.h @@ -75,6 +75,7 @@ namespace AST AST_TO_BITS, AST_TO_SIGNED, AST_TO_UNSIGNED, + AST_SELFSZ, AST_CONCAT, AST_REPLICATE, AST_BIT_NOT, @@ -91,6 +92,8 @@ namespace AST AST_SHIFT_RIGHT, AST_SHIFT_SLEFT, AST_SHIFT_SRIGHT, + AST_SHIFTX, + AST_SHIFT, AST_LT, AST_LE, AST_EQ, diff --git a/frontends/ast/genrtlil.cc b/frontends/ast/genrtlil.cc index d35335747..cdc3adc9c 100644 --- a/frontends/ast/genrtlil.cc +++ b/frontends/ast/genrtlil.cc @@ -43,12 +43,12 @@ using namespace AST_INTERNAL; // helper function for creating RTLIL code for unary operations static RTLIL::SigSpec uniop2rtlil(AstNode *that, IdString type, int result_width, const RTLIL::SigSpec &arg, bool gen_attributes = true) { - IdString name = stringf("%s$%s:%d$%d", type.c_str(), that->filename.c_str(), that->location.first_line, autoidx++); + IdString name = stringf("%s$%s:%d$%d", type.c_str(), that->filename.c_str(), that->location.first_line, autoidx++); RTLIL::Cell *cell = current_module->addCell(name, type); - cell->attributes[ID::src] = stringf("%s:%d", that->filename.c_str(), that->location.first_line); + cell->attributes[ID::src] = stringf("%s:%d.%d-%d.%d", that->filename.c_str(), that->location.first_line, that->location.first_column, that->location.last_line, that->location.last_column); RTLIL::Wire *wire = current_module->addWire(cell->name.str() + "_Y", result_width); - wire->attributes[ID::src] = stringf("%s:%d", that->filename.c_str(), that->location.first_line); + wire->attributes[ID::src] = stringf("%s:%d.%d-%d.%d", that->filename.c_str(), that->location.first_line, that->location.first_column, that->location.last_line, that->location.last_column); if (gen_attributes) for (auto &attr : that->attributes) { @@ -74,12 +74,12 @@ static void widthExtend(AstNode *that, RTLIL::SigSpec &sig, int width, bool is_s return; } - IdString name = stringf("$extend$%s:%d$%d", that->filename.c_str(), that->location.first_line, autoidx++); + IdString name = stringf("$extend$%s:%d$%d", that->filename.c_str(), that->location.first_line, autoidx++); RTLIL::Cell *cell = current_module->addCell(name, ID($pos)); - cell->attributes[ID::src] = stringf("%s:%d", that->filename.c_str(), that->location.first_line); + cell->attributes[ID::src] = stringf("%s:%d.%d-%d.%d", that->filename.c_str(), that->location.first_line, that->location.first_column, that->location.last_line, that->location.last_column); RTLIL::Wire *wire = current_module->addWire(cell->name.str() + "_Y", width); - wire->attributes[ID::src] = stringf("%s:%d", that->filename.c_str(), that->location.first_line); + wire->attributes[ID::src] = stringf("%s:%d.%d-%d.%d", that->filename.c_str(), that->location.first_line, that->location.first_column, that->location.last_line, that->location.last_column); if (that != NULL) for (auto &attr : that->attributes) { @@ -100,12 +100,12 @@ static void widthExtend(AstNode *that, RTLIL::SigSpec &sig, int width, bool is_s // helper function for creating RTLIL code for binary operations static RTLIL::SigSpec binop2rtlil(AstNode *that, IdString type, int result_width, const RTLIL::SigSpec &left, const RTLIL::SigSpec &right) { - IdString name = stringf("%s$%s:%d$%d", type.c_str(), that->filename.c_str(), that->location.first_line, autoidx++); + IdString name = stringf("%s$%s:%d$%d", type.c_str(), that->filename.c_str(), that->location.first_line, autoidx++); RTLIL::Cell *cell = current_module->addCell(name, type); - cell->attributes[ID::src] = stringf("%s:%d", that->filename.c_str(), that->location.first_line); + cell->attributes[ID::src] = stringf("%s:%d.%d-%d.%d", that->filename.c_str(), that->location.first_line, that->location.first_column, that->location.last_line, that->location.last_column); RTLIL::Wire *wire = current_module->addWire(cell->name.str() + "_Y", result_width); - wire->attributes[ID::src] = stringf("%s:%d", that->filename.c_str(), that->location.first_line); + wire->attributes[ID::src] = stringf("%s:%d.%d-%d.%d", that->filename.c_str(), that->location.first_line, that->location.first_column, that->location.last_line, that->location.last_column); for (auto &attr : that->attributes) { if (attr.second->type != AST_CONSTANT) @@ -136,10 +136,10 @@ static RTLIL::SigSpec mux2rtlil(AstNode *that, const RTLIL::SigSpec &cond, const sstr << "$ternary$" << that->filename << ":" << that->location.first_line << "$" << (autoidx++); RTLIL::Cell *cell = current_module->addCell(sstr.str(), ID($mux)); - cell->attributes[ID::src] = stringf("%s:%d", that->filename.c_str(), that->location.first_line); + cell->attributes[ID::src] = stringf("%s:%d.%d-%d.%d", that->filename.c_str(), that->location.first_line, that->location.first_column, that->location.last_line, that->location.last_column); RTLIL::Wire *wire = current_module->addWire(cell->name.str() + "_Y", left.size()); - wire->attributes[ID::src] = stringf("%s:%d", that->filename.c_str(), that->location.first_line); + wire->attributes[ID::src] = stringf("%s:%d.%d-%d.%d", that->filename.c_str(), that->location.first_line, that->location.first_column, that->location.last_line, that->location.last_column); for (auto &attr : that->attributes) { if (attr.second->type != AST_CONSTANT) @@ -171,7 +171,7 @@ struct AST_INTERNAL::LookaheadRewriter for (auto c : node->id2ast->children) wire->children.push_back(c->clone()); wire->str = stringf("$lookahead%s$%d", node->str.c_str(), autoidx++); - wire->attributes["\\nosync"] = AstNode::mkconst_int(1, false); + wire->attributes[ID::nosync] = AstNode::mkconst_int(1, false); wire->is_logic = true; while (wire->simplify(true, false, false, 1, -1, false, false)) { } current_ast_mod->children.push_back(wire); @@ -809,6 +809,11 @@ void AstNode::detectSignWidthWorker(int &width_hint, bool &sign_hint, bool *foun sign_hint = false; break; + case AST_SELFSZ: + sub_width_hint = 0; + children.at(0)->detectSignWidthWorker(sub_width_hint, sign_hint); + break; + case AST_CONCAT: for (auto child : children) { sub_width_hint = 0; @@ -856,6 +861,8 @@ void AstNode::detectSignWidthWorker(int &width_hint, bool &sign_hint, bool *foun case AST_SHIFT_RIGHT: case AST_SHIFT_SLEFT: case AST_SHIFT_SRIGHT: + case AST_SHIFTX: + case AST_SHIFT: case AST_POW: children[0]->detectSignWidthWorker(width_hint, sign_hint, found_real); break; @@ -923,7 +930,7 @@ void AstNode::detectSignWidthWorker(int &width_hint, bool &sign_hint, bool *foun } break; } - /* fall through */ + YS_FALLTHROUGH // everything should have been handled above -> print error if not. default: @@ -1019,7 +1026,7 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) if (GetSize(children) >= 1 && children[0]->type == AST_CONSTANT) { current_module->parameter_default_values[str] = children[0]->asParaConst(); } - /* fall through */ + YS_FALLTHROUGH case AST_LOCALPARAM: if (flag_pwires) { @@ -1048,7 +1055,7 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) if (!range_valid) log_file_error(filename, location.first_line, "Signal `%s' with non-constant width!\n", str.c_str()); - if (!(range_left >= range_right || (range_left == -1 && range_right == 0))) + if (!(range_left + 1 >= range_right)) log_file_error(filename, location.first_line, "Signal `%s' with invalid width range %d!\n", str.c_str(), range_left - range_right + 1); RTLIL::Wire *wire = current_module->addWire(str, range_left - range_right + 1); @@ -1205,13 +1212,18 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) AstNode *fake_ast = new AstNode(AST_NONE, clone(), children[0]->children.size() >= 2 ? children[0]->children[1]->clone() : children[0]->children[0]->clone()); fake_ast->children[0]->delete_children(); - RTLIL::SigSpec shift_val = fake_ast->children[1]->genRTLIL(); + + int fake_ast_width = 0; + bool fake_ast_sign = true; + fake_ast->children[1]->detectSignWidth(fake_ast_width, fake_ast_sign); + RTLIL::SigSpec shift_val = fake_ast->children[1]->genRTLIL(fake_ast_width, fake_ast_sign); + if (id2ast->range_right != 0) { - shift_val = current_module->Sub(NEW_ID, shift_val, id2ast->range_right, fake_ast->children[1]->is_signed); + shift_val = current_module->Sub(NEW_ID, shift_val, id2ast->range_right, fake_ast_sign); fake_ast->children[1]->is_signed = true; } if (id2ast->range_swapped) { - shift_val = current_module->Sub(NEW_ID, RTLIL::SigSpec(source_width - width), shift_val, fake_ast->children[1]->is_signed); + shift_val = current_module->Sub(NEW_ID, RTLIL::SigSpec(source_width - width), shift_val, fake_ast_sign); fake_ast->children[1]->is_signed = true; } if (GetSize(shift_val) >= 32) @@ -1265,7 +1277,8 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) // just pass thru the signal. the parent will evaluate the is_signed property and interpret the SigSpec accordingly case AST_TO_SIGNED: - case AST_TO_UNSIGNED: { + case AST_TO_UNSIGNED: + case AST_SELFSZ: { RTLIL::SigSpec sig = children[0]->genRTLIL(); if (sig.size() < width_hint) sig.extend_u0(width_hint, sign_hint); @@ -1356,6 +1369,8 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) if (0) { case AST_SHIFT_RIGHT: type_name = ID($shr); } if (0) { case AST_SHIFT_SLEFT: type_name = ID($sshl); } if (0) { case AST_SHIFT_SRIGHT: type_name = ID($sshr); } + if (0) { case AST_SHIFTX: type_name = ID($shiftx); } + if (0) { case AST_SHIFT: type_name = ID($shift); } { if (width_hint < 0) detectSignWidth(width_hint, sign_hint); @@ -1500,10 +1515,10 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) sstr << "$memrd$" << str << "$" << filename << ":" << location.first_line << "$" << (autoidx++); RTLIL::Cell *cell = current_module->addCell(sstr.str(), ID($memrd)); - cell->attributes[ID::src] = stringf("%s:%d", filename.c_str(), location.first_line); + cell->attributes[ID::src] = stringf("%s:%d.%d-%d.%d", filename.c_str(), location.first_line, location.first_column, location.last_line, location.last_column); RTLIL::Wire *wire = current_module->addWire(cell->name.str() + "_DATA", current_module->memories[str]->width); - wire->attributes[ID::src] = stringf("%s:%d", filename.c_str(), location.first_line); + wire->attributes[ID::src] = stringf("%s:%d.%d-%d.%d", filename.c_str(), location.first_line, location.first_column, location.last_line, location.last_column); int mem_width, mem_size, addr_bits; is_signed = id2ast->is_signed; @@ -1807,7 +1822,8 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint) is_signed = sign_hint; return SigSpec(wire); } - } /* fall through */ + } + YS_FALLTHROUGH // everything should have been handled above -> print error if not. default: diff --git a/frontends/ast/simplify.cc b/frontends/ast/simplify.cc index 9453937e3..3d690c1f5 100644 --- a/frontends/ast/simplify.cc +++ b/frontends/ast/simplify.cc @@ -91,7 +91,7 @@ std::string AstNode::process_format_str(const std::string &sformat, int next_arg case 'D': if (got_len) goto unsupported_format; - /* fall through */ + YS_FALLTHROUGH case 'x': case 'X': if (next_arg >= GetSize(children)) @@ -608,6 +608,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, case AST_TO_BITS: case AST_TO_SIGNED: case AST_TO_UNSIGNED: + case AST_SELFSZ: case AST_CONCAT: case AST_REPLICATE: case AST_REDUCE_AND: @@ -920,11 +921,11 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, range_swapped = templ->range_swapped; range_left = templ->range_left; range_right = templ->range_right; - attributes["\\wiretype"] = mkconst_str(resolved_type->str); + attributes[ID::wiretype] = mkconst_str(resolved_type->str); //check if enum - if (templ->attributes.count("\\enum_type")){ + if (templ->attributes.count(ID::enum_type)){ //get reference to enum node: - std::string enum_type = templ->attributes["\\enum_type"]->str.c_str(); + const std::string &enum_type = templ->attributes[ID::enum_type]->str; // log("enum_type=%s (count=%lu)\n", enum_type.c_str(), current_scope.count(enum_type)); // log("current scope:\n"); // for (auto &it : current_scope) @@ -972,7 +973,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, RTLIL::Const val = enum_item->children[0]->bitsAsConst(width, is_signed); enum_item_str.append(val.as_string()); //set attribute for available val to enum item name mappings - attributes[enum_item_str.c_str()] = mkconst_str(enum_item->str); + attributes[enum_item_str] = mkconst_str(enum_item->str); } } @@ -1021,7 +1022,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, range_swapped = templ->range_swapped; range_left = templ->range_left; range_right = templ->range_right; - attributes["\\wiretype"] = mkconst_str(resolved_type->str); + attributes[ID::wiretype] = mkconst_str(resolved_type->str); for (auto template_child : templ->children) children.push_back(template_child->clone()); did_something = true; @@ -1079,7 +1080,7 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, } if (old_range_valid != range_valid) did_something = true; - if (range_valid && range_left >= 0 && range_right > range_left) { + if (range_valid && range_right > range_left) { int tmp = range_right; range_right = range_left; range_left = tmp; @@ -1097,6 +1098,25 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, range_swapped = children[0]->range_swapped; range_left = children[0]->range_left; range_right = children[0]->range_right; + bool force_upto = false, force_downto = false; + if (attributes.count(ID::force_upto)) { + AstNode *val = attributes[ID::force_upto]; + if (val->type != AST_CONSTANT) + log_file_error(filename, location.first_line, "Attribute `force_upto' with non-constant value!\n"); + force_upto = val->asAttrConst().as_bool(); + } + if (attributes.count(ID::force_downto)) { + AstNode *val = attributes[ID::force_downto]; + if (val->type != AST_CONSTANT) + log_file_error(filename, location.first_line, "Attribute `force_downto' with non-constant value!\n"); + force_downto = val->asAttrConst().as_bool(); + } + if (force_upto && force_downto) + log_file_error(filename, location.first_line, "Attributes `force_downto' and `force_upto' cannot be both set!\n"); + if ((force_upto && !range_swapped) || (force_downto && range_swapped)) { + std::swap(range_left, range_right); + range_swapped = force_upto; + } } } else { if (!range_valid) @@ -1739,8 +1759,10 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, AstNode *node = children_list[1]; if (op_type != AST_POS) - for (size_t i = 2; i < children_list.size(); i++) + for (size_t i = 2; i < children_list.size(); i++) { node = new AstNode(op_type, node, children_list[i]); + node->location = location; + } if (invert_results) node = new AstNode(AST_BIT_NOT, node); @@ -1786,7 +1808,18 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, result_width = abs(int(left_at_zero_ast->integer - right_at_zero_ast->integer)) + 1; } - if (0) + bool use_case_method = false; + + if (children[0]->id2ast->attributes.count(ID::nowrshmsk)) { + AstNode *node = children[0]->id2ast->attributes.at(ID::nowrshmsk); + while (node->simplify(true, false, false, stage, -1, false, false)) { } + if (node->type != AST_CONSTANT) + log_file_error(filename, location.first_line, "Non-constant value for `nowrshmsk' attribute on `%s'!\n", children[0]->id2ast->str.c_str()); + if (node->asAttrConst().as_bool()) + use_case_method = true; + } + + if (use_case_method) { // big case block @@ -1794,10 +1827,10 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, newNode = new AstNode(AST_CASE, shift_expr); for (int i = 0; i < source_width; i++) { int start_bit = children[0]->id2ast->range_right + i; + int end_bit = std::min(start_bit+result_width,source_width) - 1; AstNode *cond = new AstNode(AST_COND, mkconst_int(start_bit, true)); AstNode *lvalue = children[0]->clone(); lvalue->delete_children(); - int end_bit = std::min(start_bit+result_width,source_width) - 1; lvalue->children.push_back(new AstNode(AST_RANGE, mkconst_int(end_bit, true), mkconst_int(start_bit, true))); cond->children.push_back(new AstNode(AST_BLOCK, new AstNode(type, lvalue, children[1]->clone()))); @@ -1810,14 +1843,14 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, AstNode *wire_mask = new AstNode(AST_WIRE, new AstNode(AST_RANGE, mkconst_int(source_width-1, true), mkconst_int(0, true))); wire_mask->str = stringf("$bitselwrite$mask$%s:%d$%d", filename.c_str(), location.first_line, autoidx++); - wire_mask->attributes["\\nosync"] = AstNode::mkconst_int(1, false); + wire_mask->attributes[ID::nosync] = AstNode::mkconst_int(1, false); wire_mask->is_logic = true; while (wire_mask->simplify(true, false, false, 1, -1, false, false)) { } current_ast_mod->children.push_back(wire_mask); AstNode *wire_data = new AstNode(AST_WIRE, new AstNode(AST_RANGE, mkconst_int(source_width-1, true), mkconst_int(0, true))); wire_data->str = stringf("$bitselwrite$data$%s:%d$%d", filename.c_str(), location.first_line, autoidx++); - wire_data->attributes["\\nosync"] = AstNode::mkconst_int(1, false); + wire_data->attributes[ID::nosync] = AstNode::mkconst_int(1, false); wire_data->is_logic = true; while (wire_data->simplify(true, false, false, 1, -1, false, false)) { } current_ast_mod->children.push_back(wire_data); @@ -1844,11 +1877,40 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, AstNode *shamt = shift_expr; - newNode->children.push_back(new AstNode(AST_ASSIGN_EQ, ref_mask->clone(), - new AstNode(AST_SHIFT_LEFT, mkconst_bits(std::vector<RTLIL::State>(result_width, State::S1), false), shamt->clone()))); - newNode->children.push_back(new AstNode(AST_ASSIGN_EQ, ref_data->clone(), - new AstNode(AST_SHIFT_LEFT, new AstNode(AST_BIT_AND, mkconst_bits(std::vector<RTLIL::State>(result_width, State::S1), false), children[1]->clone()), shamt))); - newNode->children.push_back(new AstNode(type, lvalue, new AstNode(AST_BIT_OR, new AstNode(AST_BIT_AND, old_data, new AstNode(AST_BIT_NOT, ref_mask)), ref_data))); + int shamt_width_hint = 0; + bool shamt_sign_hint = true; + shamt->detectSignWidth(shamt_width_hint, shamt_sign_hint); + + int start_bit = children[0]->id2ast->range_right; + bool use_shift = shamt_sign_hint; + + if (start_bit != 0) { + shamt = new AstNode(AST_SUB, shamt, mkconst_int(start_bit, true)); + use_shift = true; + } + + AstNode *t; + + t = mkconst_bits(std::vector<RTLIL::State>(result_width, State::S1), false); + if (use_shift) + t = new AstNode(AST_SHIFT, t, new AstNode(AST_NEG, shamt->clone())); + else + t = new AstNode(AST_SHIFT_LEFT, t, shamt->clone()); + t = new AstNode(AST_ASSIGN_EQ, ref_mask->clone(), t); + newNode->children.push_back(t); + + t = new AstNode(AST_BIT_AND, mkconst_bits(std::vector<RTLIL::State>(result_width, State::S1), false), children[1]->clone()); + if (use_shift) + t = new AstNode(AST_SHIFT, t, new AstNode(AST_NEG, shamt)); + else + t = new AstNode(AST_SHIFT_LEFT, t, shamt); + t = new AstNode(AST_ASSIGN_EQ, ref_data->clone(), t); + newNode->children.push_back(t); + + t = new AstNode(AST_BIT_AND, old_data, new AstNode(AST_BIT_NOT, ref_mask)); + t = new AstNode(AST_BIT_OR, t, ref_data); + t = new AstNode(type, lvalue, t); + newNode->children.push_back(t); } goto apply_newNode; @@ -2637,7 +2699,7 @@ skip_dynamic_range_lvalue_expansion:; bool recommend_const_eval = false; bool require_const_eval = in_param ? false : has_const_only_constructs(recommend_const_eval); - if ((in_param || recommend_const_eval || require_const_eval) && !decl->attributes.count("\\via_celltype")) + if ((in_param || recommend_const_eval || require_const_eval) && !decl->attributes.count(ID::via_celltype)) { bool all_args_const = true; for (auto child : children) { @@ -2696,9 +2758,9 @@ skip_dynamic_range_lvalue_expansion:; goto replace_fcall_with_id; } - if (decl->attributes.count("\\via_celltype")) + if (decl->attributes.count(ID::via_celltype)) { - std::string celltype = decl->attributes.at("\\via_celltype")->asAttrConst().decode_string(); + std::string celltype = decl->attributes.at(ID::via_celltype)->asAttrConst().decode_string(); std::string outport = str; if (celltype.find(' ') != std::string::npos) { @@ -2792,7 +2854,7 @@ skip_dynamic_range_lvalue_expansion:; wire->is_reg = true; wire->attributes[ID::nosync] = AstNode::mkconst_int(1, false); if (child->type == AST_ENUM_ITEM) - wire->attributes["\\enum_base_type"] = child->attributes["\\enum_base_type"]; + wire->attributes[ID::enum_base_type] = child->attributes[ID::enum_base_type]; wire_cache[child->str] = wire; @@ -3024,6 +3086,7 @@ replace_fcall_later:; } } break; + if (0) { case AST_SELFSZ: const_func = RTLIL::const_pos; } if (0) { case AST_POS: const_func = RTLIL::const_pos; } if (0) { case AST_NEG: const_func = RTLIL::const_neg; } if (children[0]->type == AST_CONSTANT) { @@ -3032,10 +3095,10 @@ replace_fcall_later:; } else if (children[0]->isConst()) { newNode = new AstNode(AST_REALVALUE); - if (type == AST_POS) - newNode->realvalue = +children[0]->asReal(sign_hint); - else + if (type == AST_NEG) newNode->realvalue = -children[0]->asReal(sign_hint); + else + newNode->realvalue = +children[0]->asReal(sign_hint); } break; case AST_TERNARY: @@ -4092,7 +4155,7 @@ void AstNode::allocateDefaultEnumValues() int last_enum_int = -1; for (auto node : children) { log_assert(node->type==AST_ENUM_ITEM); - node->attributes["\\enum_base_type"] = mkconst_str(str); + node->attributes[ID::enum_base_type] = mkconst_str(str); for (size_t i = 0; i < node->children.size(); i++) { switch (node->children[i]->type) { case AST_NONE: diff --git a/frontends/ilang/ilang_lexer.l b/frontends/ilang/ilang_lexer.l index 62f53d18e..3362ed641 100644 --- a/frontends/ilang/ilang_lexer.l +++ b/frontends/ilang/ilang_lexer.l @@ -91,8 +91,10 @@ USING_YOSYS_NAMESPACE [0-9]+'[01xzm-]* { rtlil_frontend_ilang_yylval.string = strdup(yytext); return TOK_VALUE; } -?[0-9]+ { char *end = nullptr; + errno = 0; long value = strtol(yytext, &end, 10); - if (end != yytext + strlen(yytext)) + log_assert(end == yytext + strlen(yytext)); + if (errno == ERANGE) return TOK_INVALID; // literal out of range of long if (value < INT_MIN || value > INT_MAX) return TOK_INVALID; // literal out of range of int (relevant mostly for LP64 platforms) diff --git a/frontends/ilang/ilang_parser.y b/frontends/ilang/ilang_parser.y index 8e21fb176..118f13de9 100644 --- a/frontends/ilang/ilang_parser.y +++ b/frontends/ilang/ilang_parser.y @@ -107,16 +107,16 @@ module: delete_current_module = false; if (current_design->has($2)) { RTLIL::Module *existing_mod = current_design->module($2); - if (!flag_overwrite && (flag_lib || (attrbuf.count("\\blackbox") && attrbuf.at("\\blackbox").as_bool()))) { + if (!flag_overwrite && (flag_lib || (attrbuf.count(ID::blackbox) && attrbuf.at(ID::blackbox).as_bool()))) { log("Ignoring blackbox re-definition of module %s.\n", $2); delete_current_module = true; - } else if (!flag_nooverwrite && !flag_overwrite && !existing_mod->get_bool_attribute("\\blackbox")) { + } else if (!flag_nooverwrite && !flag_overwrite && !existing_mod->get_bool_attribute(ID::blackbox)) { rtlil_frontend_ilang_yyerror(stringf("ilang error: redefinition of module %s.", $2).c_str()); } else if (flag_nooverwrite) { log("Ignoring re-definition of module %s.\n", $2); delete_current_module = true; } else { - log("Replacing existing%s module %s.\n", existing_mod->get_bool_attribute("\\blackbox") ? " blackbox" : "", $2); + log("Replacing existing%s module %s.\n", existing_mod->get_bool_attribute(ID::blackbox) ? " blackbox" : "", $2); current_design->remove(existing_mod); } } diff --git a/frontends/verilog/verilog_frontend.cc b/frontends/verilog/verilog_frontend.cc index 6879e0943..26abe49b5 100644 --- a/frontends/verilog/verilog_frontend.cc +++ b/frontends/verilog/verilog_frontend.cc @@ -48,7 +48,7 @@ static void error_on_dpi_function(AST::AstNode *node) error_on_dpi_function(child); } -static void add_package_types(std::map<std::string, AST::AstNode *> &user_types, std::vector<AST::AstNode *> &package_list) +static void add_package_types(dict<std::string, AST::AstNode *> &user_types, std::vector<AST::AstNode *> &package_list) { // prime the parser's user type lookup table with the package qualified names // of typedefed names in the packages seen so far. diff --git a/frontends/verilog/verilog_frontend.h b/frontends/verilog/verilog_frontend.h index 444cc7297..aa7881038 100644 --- a/frontends/verilog/verilog_frontend.h +++ b/frontends/verilog/verilog_frontend.h @@ -50,7 +50,7 @@ namespace VERILOG_FRONTEND extern std::vector<UserTypeMap *> user_type_stack; // names of package typedef'ed types - extern std::map<std::string, AST::AstNode*> pkg_user_types; + extern dict<std::string, AST::AstNode*> pkg_user_types; // state of `default_nettype extern bool default_nettype_wire; diff --git a/frontends/verilog/verilog_lexer.l b/frontends/verilog/verilog_lexer.l index f6a3ac4db..02fa0031b 100644 --- a/frontends/verilog/verilog_lexer.l +++ b/frontends/verilog/verilog_lexer.l @@ -48,16 +48,18 @@ USING_YOSYS_NAMESPACE using namespace AST; using namespace VERILOG_FRONTEND; +#define YYSTYPE FRONTEND_VERILOG_YYSTYPE +#define YYLTYPE FRONTEND_VERILOG_YYLTYPE + YOSYS_NAMESPACE_BEGIN namespace VERILOG_FRONTEND { std::vector<std::string> fn_stack; std::vector<int> ln_stack; + YYLTYPE real_location; + YYLTYPE old_location; } YOSYS_NAMESPACE_END -#define YYSTYPE FRONTEND_VERILOG_YYSTYPE -#define YYLTYPE FRONTEND_VERILOG_YYLTYPE - #define SV_KEYWORD(_tok) \ if (sv_mode) return _tok; \ log("Lexer warning: The SystemVerilog keyword `%s' (at %s:%d) is not "\ @@ -73,9 +75,6 @@ YOSYS_NAMESPACE_END #define YY_INPUT(buf,result,max_size) \ result = readsome(*VERILOG_FRONTEND::lexin, buf, max_size) -YYLTYPE real_location; -YYLTYPE old_location; - #define YY_USER_ACTION \ old_location = real_location; \ real_location.first_line = real_location.last_line; \ @@ -128,7 +127,9 @@ static bool isUserType(std::string &s) %x BASED_CONST %% - int comment_caller; + // Initialise comment_caller to something to avoid a "maybe undefined" + // warning from GCC. + int comment_caller = INITIAL; <INITIAL,SYNOPSYS_TRANSLATE_OFF>"`file_push "[^\n]* { fn_stack.push_back(current_filename); diff --git a/frontends/verilog/verilog_parser.y b/frontends/verilog/verilog_parser.y index 4a5aba79e..c8223f41d 100644 --- a/frontends/verilog/verilog_parser.y +++ b/frontends/verilog/verilog_parser.y @@ -50,12 +50,12 @@ using namespace VERILOG_FRONTEND; YOSYS_NAMESPACE_BEGIN namespace VERILOG_FRONTEND { int port_counter; - std::map<std::string, int> port_stubs; - std::map<std::string, AstNode*> *attr_list, default_attr_list; - std::stack<std::map<std::string, AstNode*> *> attr_list_stack; - std::map<std::string, AstNode*> *albuf; + dict<std::string, int> port_stubs; + dict<IdString, AstNode*> *attr_list, default_attr_list; + std::stack<dict<IdString, AstNode*> *> attr_list_stack; + dict<IdString, AstNode*> *albuf; std::vector<UserTypeMap*> user_type_stack; - std::map<std::string, AstNode*> pkg_user_types; + dict<std::string, AstNode*> pkg_user_types; std::vector<AstNode*> ast_stack; struct AstNode *astbuf1, *astbuf2, *astbuf3; struct AstNode *current_function_or_task; @@ -87,7 +87,7 @@ YOSYS_NAMESPACE_END int frontend_verilog_yylex(YYSTYPE *yylval_param, YYLTYPE *yyloc_param); -static void append_attr(AstNode *ast, std::map<std::string, AstNode*> *al) +static void append_attr(AstNode *ast, dict<IdString, AstNode*> *al) { for (auto &it : *al) { if (ast->attributes.count(it.first) > 0) @@ -97,7 +97,7 @@ static void append_attr(AstNode *ast, std::map<std::string, AstNode*> *al) delete al; } -static void append_attr_clone(AstNode *ast, std::map<std::string, AstNode*> *al) +static void append_attr_clone(AstNode *ast, dict<IdString, AstNode*> *al) { for (auto &it : *al) { if (ast->attributes.count(it.first) > 0) @@ -106,7 +106,7 @@ static void append_attr_clone(AstNode *ast, std::map<std::string, AstNode*> *al) } } -static void free_attr(std::map<std::string, AstNode*> *al) +static void free_attr(dict<IdString, AstNode*> *al) { for (auto &it : *al) delete it.second; @@ -192,7 +192,7 @@ static void addRange(AstNode *parent, int msb = 31, int lsb = 0, bool isSigned = %union { std::string *string; struct YOSYS_NAMESPACE_PREFIX AST::AstNode *ast; - std::map<std::string, YOSYS_NAMESPACE_PREFIX AST::AstNode*> *al; + YOSYS_NAMESPACE_PREFIX dict<YOSYS_NAMESPACE_PREFIX RTLIL::IdString, YOSYS_NAMESPACE_PREFIX AST::AstNode*> *al; struct specify_target *specify_target_ptr; struct specify_triple *specify_triple_ptr; struct specify_rise_fall *specify_rise_fall_ptr; @@ -289,7 +289,7 @@ attr: { if (attr_list != nullptr) attr_list_stack.push(attr_list); - attr_list = new std::map<std::string, AstNode*>; + attr_list = new dict<IdString, AstNode*>; for (auto &it : default_attr_list) (*attr_list)[it.first] = it.second->clone(); } attr_opt { @@ -311,7 +311,7 @@ defattr: DEFATTR_BEGIN { if (attr_list != nullptr) attr_list_stack.push(attr_list); - attr_list = new std::map<std::string, AstNode*>; + attr_list = new dict<IdString, AstNode*>; for (auto &it : default_attr_list) delete it.second; default_attr_list.clear(); @@ -645,13 +645,13 @@ non_opt_range: } | '[' expr TOK_POS_INDEXED expr ']' { $$ = new AstNode(AST_RANGE); - AstNode *expr = new AstNode(AST_CONCAT, $2); + AstNode *expr = new AstNode(AST_SELFSZ, $2); $$->children.push_back(new AstNode(AST_SUB, new AstNode(AST_ADD, expr->clone(), $4), AstNode::mkconst_int(1, true))); $$->children.push_back(new AstNode(AST_ADD, expr, AstNode::mkconst_int(0, true))); } | '[' expr TOK_NEG_INDEXED expr ']' { $$ = new AstNode(AST_RANGE); - AstNode *expr = new AstNode(AST_CONCAT, $2); + AstNode *expr = new AstNode(AST_SELFSZ, $2); $$->children.push_back(new AstNode(AST_ADD, expr, AstNode::mkconst_int(0, true))); $$->children.push_back(new AstNode(AST_SUB, new AstNode(AST_ADD, expr->clone(), AstNode::mkconst_int(1, true)), $4)); } | @@ -853,7 +853,19 @@ task_func_port: } if (astbuf2 && astbuf2->children.size() != 2) frontend_verilog_yyerror("task/function argument range must be of the form: [<expr>:<expr>], [<expr>+:<expr>], or [<expr>-:<expr>]"); - } wire_name | wire_name; + } wire_name | + { + if (!astbuf1) { + if (!sv_mode) + frontend_verilog_yyerror("task/function argument direction missing"); + albuf = new dict<IdString, AstNode*>; + astbuf1 = new AstNode(AST_WIRE); + current_wire_rand = false; + current_wire_const = false; + astbuf1->is_input = true; + astbuf2 = NULL; + } + } wire_name; task_func_body: task_func_body behavioral_stmt | @@ -885,6 +897,7 @@ specify_item: cell->str = stringf("$specify$%d", autoidx++); cell->children.push_back(new AstNode(AST_CELLTYPE)); cell->children.back()->str = target->dat ? "$specify3" : "$specify2"; + SET_AST_NODE_LOC(cell, en_expr ? @1 : @2, @10); char oper_polarity = 0; char oper_type = oper->at(0); @@ -973,6 +986,7 @@ specify_item: cell->str = stringf("$specify$%d", autoidx++); cell->children.push_back(new AstNode(AST_CELLTYPE)); cell->children.back()->str = "$specrule"; + SET_AST_NODE_LOC(cell, @1, @14); cell->children.push_back(new AstNode(AST_PARASET, AstNode::mkconst_str(*$1))); cell->children.back()->str = "\\TYPE"; @@ -1099,8 +1113,8 @@ specify_rise_fall: $$->fall = *$4; delete $2; delete $4; - delete $6; - log_file_warning(current_filename, get_line_num(), "Path delay expressions beyond rise/fall not currently supported. Ignoring.\n"); + delete $6; + log_file_warning(current_filename, get_line_num(), "Path delay expressions beyond rise/fall not currently supported. Ignoring.\n"); } | '(' specify_triple ',' specify_triple ',' specify_triple ',' specify_triple ',' specify_triple ',' specify_triple ')' { $$ = new specify_rise_fall; @@ -1108,11 +1122,11 @@ specify_rise_fall: $$->fall = *$4; delete $2; delete $4; - delete $6; - delete $8; - delete $10; - delete $12; - log_file_warning(current_filename, get_line_num(), "Path delay expressions beyond rise/fall not currently supported. Ignoring.\n"); + delete $6; + delete $8; + delete $10; + delete $12; + log_file_warning(current_filename, get_line_num(), "Path delay expressions beyond rise/fall not currently supported. Ignoring.\n"); } | '(' specify_triple ',' specify_triple ',' specify_triple ',' specify_triple ',' specify_triple ',' specify_triple ',' specify_triple ',' specify_triple ',' specify_triple ',' specify_triple ',' specify_triple ',' specify_triple ')' { $$ = new specify_rise_fall; @@ -1120,17 +1134,17 @@ specify_rise_fall: $$->fall = *$4; delete $2; delete $4; - delete $6; - delete $8; - delete $10; - delete $12; - delete $14; - delete $16; - delete $18; - delete $20; - delete $22; - delete $24; - log_file_warning(current_filename, get_line_num(), "Path delay expressions beyond rise/fall not currently supported. Ignoring.\n"); + delete $6; + delete $8; + delete $10; + delete $12; + delete $14; + delete $16; + delete $18; + delete $20; + delete $22; + delete $24; + log_file_warning(current_filename, get_line_num(), "Path delay expressions beyond rise/fall not currently supported. Ignoring.\n"); } specify_triple: @@ -1388,7 +1402,7 @@ enum_type: TOK_ENUM { delete astbuf1; astbuf1 = tnode; tnode->type = AST_WIRE; - tnode->attributes["\\enum_type"] = AstNode::mkconst_str(astbuf2->str); + tnode->attributes[ID::enum_type] = AstNode::mkconst_str(astbuf2->str); // drop constant but keep any range delete tnode->children[0]; tnode->children.erase(tnode->children.begin()); } @@ -1747,7 +1761,9 @@ single_prim: /* no name */ { astbuf2 = astbuf1->clone(); ast_stack.back()->children.push_back(astbuf2); - } '(' cell_port_list ')'; + } '(' cell_port_list ')' { + SET_AST_NODE_LOC(astbuf2, @1, @$); + } cell_parameter_list_opt: '#' '(' cell_parameter_list ')' | /* empty */; @@ -2187,49 +2203,56 @@ assert_property: }; simple_behavioral_stmt: - lvalue '=' delay expr { - AstNode *node = new AstNode(AST_ASSIGN_EQ, $1, $4); + attr lvalue '=' delay expr { + AstNode *node = new AstNode(AST_ASSIGN_EQ, $2, $5); ast_stack.back()->children.push_back(node); - SET_AST_NODE_LOC(node, @1, @4); + SET_AST_NODE_LOC(node, @2, @5); + append_attr(node, $1); } | - lvalue TOK_INCREMENT { - AstNode *node = new AstNode(AST_ASSIGN_EQ, $1, new AstNode(AST_ADD, $1->clone(), AstNode::mkconst_int(1, true))); + attr lvalue TOK_INCREMENT { + AstNode *node = new AstNode(AST_ASSIGN_EQ, $2, new AstNode(AST_ADD, $2->clone(), AstNode::mkconst_int(1, true))); ast_stack.back()->children.push_back(node); - SET_AST_NODE_LOC(node, @1, @2); + SET_AST_NODE_LOC(node, @2, @3); + append_attr(node, $1); } | - lvalue TOK_DECREMENT { - AstNode *node = new AstNode(AST_ASSIGN_EQ, $1, new AstNode(AST_SUB, $1->clone(), AstNode::mkconst_int(1, true))); + attr lvalue TOK_DECREMENT { + AstNode *node = new AstNode(AST_ASSIGN_EQ, $2, new AstNode(AST_SUB, $2->clone(), AstNode::mkconst_int(1, true))); ast_stack.back()->children.push_back(node); - SET_AST_NODE_LOC(node, @1, @2); + SET_AST_NODE_LOC(node, @2, @3); + append_attr(node, $1); } | - lvalue OP_LE delay expr { - AstNode *node = new AstNode(AST_ASSIGN_LE, $1, $4); + attr lvalue OP_LE delay expr { + AstNode *node = new AstNode(AST_ASSIGN_LE, $2, $5); ast_stack.back()->children.push_back(node); - SET_AST_NODE_LOC(node, @1, @4); + SET_AST_NODE_LOC(node, @2, @5); + append_attr(node, $1); }; // this production creates the obligatory if-else shift/reduce conflict behavioral_stmt: defattr | assert | wire_decl | param_decl | localparam_decl | typedef_decl | non_opt_delay behavioral_stmt | - simple_behavioral_stmt ';' | ';' | - hierarchical_id attr { + simple_behavioral_stmt ';' | + attr ';' { + free_attr($1); + } | + attr hierarchical_id { AstNode *node = new AstNode(AST_TCALL); - node->str = *$1; - delete $1; + node->str = *$2; + delete $2; ast_stack.back()->children.push_back(node); ast_stack.push_back(node); - append_attr(node, $2); + append_attr(node, $1); } opt_arg_list ';'{ ast_stack.pop_back(); } | - TOK_MSG_TASKS attr { + attr TOK_MSG_TASKS { AstNode *node = new AstNode(AST_TCALL); - node->str = *$1; - delete $1; + node->str = *$2; + delete $2; ast_stack.back()->children.push_back(node); ast_stack.push_back(node); - append_attr(node, $2); + append_attr(node, $1); } opt_arg_list ';'{ ast_stack.pop_back(); } | @@ -2326,8 +2349,6 @@ behavioral_stmt: ast_stack.pop_back(); }; - ; - unique_case_attr: /* empty */ { $$ = false; @@ -2341,7 +2362,7 @@ unique_case_attr: case_attr: attr unique_case_attr { - if ($2) (*$1)["\\parallel_case"] = AstNode::mkconst_int(1, false); + if ($2) (*$1)[ID::parallel_case] = AstNode::mkconst_int(1, false); $$ = $1; }; @@ -2422,7 +2443,7 @@ gen_case_item: } case_select { case_type_stack.push_back(0); SET_AST_NODE_LOC(ast_stack.back(), @2, @2); - } gen_stmt_or_null { + } gen_stmt_block { case_type_stack.pop_back(); ast_stack.pop_back(); }; @@ -2514,7 +2535,10 @@ module_gen_body: /* empty */; gen_stmt_or_module_body_stmt: - gen_stmt | module_body_stmt; + gen_stmt | module_body_stmt | + attr ';' { + free_attr($1); + }; // this production creates the obligatory if-else shift/reduce conflict gen_stmt: @@ -2533,7 +2557,12 @@ gen_stmt: ast_stack.back()->children.push_back(node); ast_stack.push_back(node); ast_stack.back()->children.push_back($3); - } gen_stmt_block opt_gen_else { + AstNode *block = new AstNode(AST_GENBLOCK); + ast_stack.back()->children.push_back(block); + ast_stack.push_back(block); + } gen_stmt_block { + ast_stack.pop_back(); + } opt_gen_else { SET_AST_NODE_LOC(ast_stack.back(), @1, @7); ast_stack.pop_back(); } | @@ -2581,11 +2610,8 @@ gen_stmt_block: ast_stack.pop_back(); }; -gen_stmt_or_null: - gen_stmt_block | ';'; - opt_gen_else: - TOK_ELSE gen_stmt_or_null | /* empty */ %prec FAKE_THEN; + TOK_ELSE gen_stmt_block | /* empty */ %prec FAKE_THEN; expr: basic_expr { |