diff --git a/PExpr.cc b/PExpr.cc index 7e0e3693f..bce998419 100644 --- a/PExpr.cc +++ b/PExpr.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998-2012 Stephen Williams + * Copyright (c) 1998-2016 Stephen Williams * Copyright CERN 2013 / Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it @@ -137,7 +137,7 @@ bool PEBinary::has_aa_term(Design*des, NetScope*scope) const return left_->has_aa_term(des, scope) || right_->has_aa_term(des, scope); } -PECastSize::PECastSize(unsigned si, PExpr*b) +PECastSize::PECastSize(PExpr*si, PExpr*b) : size_(si), base_(b) { } diff --git a/PExpr.h b/PExpr.h index 55f0fb237..adf17a137 100644 --- a/PExpr.h +++ b/PExpr.h @@ -1,7 +1,7 @@ #ifndef IVL_PExpr_H #define IVL_PExpr_H /* - * Copyright (c) 1998-2014 Stephen Williams + * Copyright (c) 1998-2016 Stephen Williams * Copyright CERN 2013 / Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it @@ -971,7 +971,7 @@ class PECallFunction : public PExpr { class PECastSize : public PExpr { public: - explicit PECastSize(unsigned expr_wid, PExpr*base); + explicit PECastSize(PExpr*size, PExpr*base); ~PECastSize(); void dump(ostream &out) const; @@ -984,7 +984,7 @@ class PECastSize : public PExpr { width_mode_t&mode); private: - unsigned size_; + PExpr* size_; PExpr* base_; }; diff --git a/elab_expr.cc b/elab_expr.cc index 6d8c1236d..84a256a4b 100644 --- a/elab_expr.cc +++ b/elab_expr.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999-2015 Stephen Williams (steve@icarus.com) + * Copyright (c) 1999-2016 Stephen Williams (steve@icarus.com) * Copyright CERN 2013 / Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it @@ -2517,22 +2517,54 @@ NetExpr* PECallFunction::elaborate_expr_method_(Design*des, NetScope*scope, unsigned PECastSize::test_width(Design*des, NetScope*scope, width_mode_t&) { - expr_width_ = size_; + ivl_assert(*this, size_); + ivl_assert(*this, base_); + + NetExpr*size_ex = elab_and_eval(des, scope, size_, -1, true); + NetEConst*size_ce = dynamic_cast(size_ex); + expr_width_ = size_ce ? size_ce->value().as_ulong() : 0; + delete size_ex; + if (expr_width_ == 0) { + cerr << get_fileline() << ": error: Cast size expression " + "must be constant and greater than zero." << endl; + des->errors += 1; + return 0; + } width_mode_t tmp_mode = PExpr::SIZED; base_->test_width(des, scope, tmp_mode); - return size_; + if (!type_is_vectorable(base_->expr_type())) { + cerr << get_fileline() << ": error: Cast base expression " + "must be a vector type." << endl; + des->errors += 1; + return 0; + } + + expr_type_ = base_->expr_type(); + min_width_ = expr_width_; + signed_flag_ = base_->has_sign(); + + return expr_width_; } NetExpr* PECastSize::elaborate_expr(Design*des, NetScope*scope, - unsigned, unsigned) const + unsigned expr_wid, unsigned flags) const { - NetExpr*sub = base_->elaborate_expr(des, scope, base_->expr_width(), NO_FLAGS); - NetESelect*sel = new NetESelect(sub, 0, size_); - sel->set_line(*this); + flags &= ~SYS_TASK_ARG; // don't propagate the SYS_TASK_ARG flag - return sel; + ivl_assert(*this, size_); + ivl_assert(*this, base_); + + NetExpr*sub = base_->elaborate_expr(des, scope, base_->expr_width(), flags); + + // Perform the cast. The extension method (zero/sign), if needed, + // depends on the type of the base expression. + NetExpr*tmp = cast_to_width(sub, expr_width_, base_->has_sign(), *this); + + // Pad up to the expression width. The extension method (zero/sign) + // depends on the type of enclosing expression. + return pad_to_width(tmp, expr_wid, signed_flag_, *this); } unsigned PECastType::test_width(Design*des, NetScope*scope, width_mode_t&wid) diff --git a/netmisc.h b/netmisc.h index 2da355359..7d8f6620d 100644 --- a/netmisc.h +++ b/netmisc.h @@ -1,7 +1,7 @@ #ifndef IVL_netmisc_H #define IVL_netmisc_H /* - * Copyright (c) 1999-2014 Stephen Williams (steve@icarus.com) + * Copyright (c) 1999-2016 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form under the terms of the GNU @@ -66,6 +66,27 @@ inline NetScope* symbol_search(const LineInfo*li, * enough. */ extern NetExpr*pad_to_width(NetExpr*expr, unsigned wid, const LineInfo&info); + +/* + * This function transforms an expression by either zero or sign extending + * the high bits until the expression has the desired width. This may mean + * not transforming the expression at all, if it is already wide enough. + * The extension method and the returned expression type is determined by + * signed_flag. + */ +extern NetExpr*pad_to_width(NetExpr*expr, unsigned wid, bool signed_flag, + const LineInfo&info); + +/* + * This function transforms an expression by either zero or sign extending + * or discarding the high bits until the expression has the desired width. + * This may mean not transforming the expression at all, if it is already + * the correct width. The extension method (if needed) and the returned + * expression type is determined by signed_flag. + */ +extern NetExpr*cast_to_width(NetExpr*expr, unsigned wid, bool signed_flag, + const LineInfo&info); + extern NetNet*pad_to_width(Design*des, NetNet*n, unsigned w, const LineInfo&info); diff --git a/pad_to_width.cc b/pad_to_width.cc index 32ba6c61d..85f8d74e4 100644 --- a/pad_to_width.cc +++ b/pad_to_width.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999-2011 Stephen Williams (steve@icarus.com) + * Copyright (c) 1999-2016 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form under the terms of the GNU @@ -51,6 +51,54 @@ NetExpr*pad_to_width(NetExpr*expr, unsigned wid, const LineInfo&info) return tmp; } +NetExpr*pad_to_width(NetExpr*expr, unsigned wid, bool signed_flag, + const LineInfo&info) +{ + if (wid <= expr->expr_width()) { + expr->cast_signed(signed_flag); + return expr; + } + + /* If the expression is a const, then replace it with a wider + const. This is a more efficient result. */ + if (NetEConst*tmp = dynamic_cast(expr)) { + verinum oval = tmp->value(); + oval.has_sign(signed_flag); + oval = pad_to_width(oval, wid); + tmp = new NetEConst(oval); + tmp->set_line(info); + delete expr; + return tmp; + } + + NetESelect*tmp = new NetESelect(expr, 0, wid); + tmp->cast_signed(signed_flag); + tmp->set_line(info); + return tmp; +} + +NetExpr*cast_to_width(NetExpr*expr, unsigned wid, bool signed_flag, + const LineInfo&info) +{ + /* If the expression is a const, then replace it with a new + const. This is a more efficient result. */ + if (NetEConst*tmp = dynamic_cast(expr)) { + tmp->cast_signed(signed_flag); + if (wid != tmp->expr_width()) { + tmp = new NetEConst(verinum(tmp->value(), wid)); + tmp->set_line(info); + delete expr; + } + return tmp; + } + + NetESelect*tmp = new NetESelect(expr, 0, wid); + tmp->cast_signed(signed_flag); + tmp->set_line(info); + + return tmp; +} + /* * Pad a NetNet to the desired vector width by concatenating a * NetConst of constant zeros. Use a NetConcat node to do the diff --git a/parse.y b/parse.y index c6753deea..f2845750d 100644 --- a/parse.y +++ b/parse.y @@ -613,7 +613,8 @@ static void current_function_set_statement(const YYLTYPE&loc, vector %type gate_instance_list %type hierarchy_identifier implicit_class_handle -%type assignment_pattern expression expr_primary expr_mintypmax +%type assignment_pattern expression expr_mintypmax +%type expr_primary_or_typename expr_primary %type class_new dynamic_array_new %type inc_or_dec_expression inside_expression lpvalue %type branch_probe_expression streaming_concatenation @@ -3083,7 +3084,7 @@ branch_probe_expression ; expression - : expr_primary + : expr_primary_or_typename { $$ = $1; } | inc_or_dec_expression { $$ = $1; } @@ -3378,6 +3379,20 @@ expression_list_proper } ; +expr_primary_or_typename + : expr_primary + + /* There are a few special cases (notably $bits argument) where the + expression may be a type name. Let the elaborator sort this out. */ + | TYPE_IDENTIFIER + { PETypename*tmp = new PETypename($1.type); + FILE_NAME(tmp,@1); + $$ = tmp; + delete[]$1.text; + } + + ; + expr_primary : number { assert($1); @@ -3419,15 +3434,6 @@ expr_primary delete[]$1; } - /* There are a few special cases (notably $bits argument) where the - expression may be a type name. Let the elaborator sort this out. */ - | TYPE_IDENTIFIER - { PETypename*tmp = new PETypename($1.type); - FILE_NAME(tmp,@1); - $$ = tmp; - delete[]$1.text; - } - /* The hierarchy_identifier rule matches simple identifiers as well as indexed arrays and part selects */ @@ -3731,12 +3737,11 @@ expr_primary /* Cast expressions are primaries */ - | DEC_NUMBER '\'' '(' expression ')' + | expr_primary '\'' '(' expression ')' { PExpr*base = $4; if (gn_system_verilog()) { - PECastSize*tmp = new PECastSize($1->as_ulong(), base); + PECastSize*tmp = new PECastSize($1, base); FILE_NAME(tmp, @1); - delete $1; $$ = tmp; } else { yyerror(@1, "error: Size cast requires SystemVerilog."); diff --git a/pform_dump.cc b/pform_dump.cc index 39620da9e..bc44c0519 100644 --- a/pform_dump.cc +++ b/pform_dump.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998-2014 Stephen Williams (steve@icarus.com) + * Copyright (c) 1998-2016 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form under the terms of the GNU @@ -318,7 +318,7 @@ void PECallFunction::dump(ostream &out) const void PECastSize::dump(ostream &out) const { - out << size_ << "'("; + out << *size_ << "'("; base_->dump(out); out << ")"; }