From ab658f56a56ea548b1f293a4a3ada68feb11b417 Mon Sep 17 00:00:00 2001 From: Ian Lance Taylor Date: Fri, 7 Jun 2019 13:40:26 +0000 Subject: [PATCH] compiler: do simple deadcode elimination Normally the backend will do deadcode elimination and this is sufficient. However, the escape analysis operates on the AST that may have deadcode, and may cause things to escape that otherwise do not. This CL adds a simple deadcode elimination, run before the escape analysis. Reviewed-on: https://go-review.googlesource.com/c/gofrontend/+/181080 From-SVN: r272043 --- gcc/go/gofrontend/MERGE | 2 +- gcc/go/gofrontend/expressions.cc | 129 +++++++++++++++++++++++++++++++ gcc/go/gofrontend/expressions.h | 21 +++++ gcc/go/gofrontend/go.cc | 3 + gcc/go/gofrontend/gogo.cc | 74 ++++++++++++++++++ gcc/go/gofrontend/gogo.h | 4 + gcc/go/gofrontend/statements.h | 8 ++ 7 files changed, 240 insertions(+), 1 deletion(-) diff --git a/gcc/go/gofrontend/MERGE b/gcc/go/gofrontend/MERGE index 7f687c8cc00..428b59a08fe 100644 --- a/gcc/go/gofrontend/MERGE +++ b/gcc/go/gofrontend/MERGE @@ -1,4 +1,4 @@ -e76c26059585433ce44e50cd7f8f504c6676f453 +46329dd9e6473fff46df6b310c11116d1558e470 The first line of this file holds the git revision number of the last merge done from the gofrontend repository. diff --git a/gcc/go/gofrontend/expressions.cc b/gcc/go/gofrontend/expressions.cc index 273c8f074c7..3481d000868 100644 --- a/gcc/go/gofrontend/expressions.cc +++ b/gcc/go/gofrontend/expressions.cc @@ -1763,6 +1763,13 @@ class Boolean_expression : public Expression do_is_zero_value() const { return this->val_ == false; } + bool + do_boolean_constant_value(bool* val) const + { + *val = this->val_; + return true; + } + bool do_is_static_initializer() const { return true; } @@ -3132,6 +3139,9 @@ class Const_expression : public Expression bool do_string_constant_value(std::string* val) const; + bool + do_boolean_constant_value(bool* val) const; + Type* do_type(); @@ -3250,6 +3260,21 @@ Const_expression::do_string_constant_value(std::string* val) const return ok; } +bool +Const_expression::do_boolean_constant_value(bool* val) const +{ + if (this->seen_) + return false; + + Expression* e = this->constant_->const_value()->expr(); + + this->seen_ = true; + bool ok = e->boolean_constant_value(val); + this->seen_ = false; + + return ok; +} + // Return the type of the const reference. Type* @@ -3841,6 +3866,16 @@ Type_conversion_expression::do_string_constant_value(std::string* val) const return false; } +// Return the constant boolean value if there is one. + +bool +Type_conversion_expression::do_boolean_constant_value(bool* val) const +{ + if (!this->type_->is_boolean_type()) + return false; + return this->expr_->boolean_constant_value(val); +} + // Determine the resulting type of the conversion. void @@ -4710,6 +4745,20 @@ Unary_expression::do_numeric_constant_value(Numeric_constant* nc) const nc, &issued_error); } +// Return the boolean constant value of a unary expression, if it has one. + +bool +Unary_expression::do_boolean_constant_value(bool* val) const +{ + if (this->op_ == OPERATOR_NOT + && this->expr_->boolean_constant_value(val)) + { + *val = !*val; + return true; + } + return false; +} + // Return the type of a unary expression. Type* @@ -6187,6 +6236,86 @@ Binary_expression::do_numeric_constant_value(Numeric_constant* nc) const this->location(), nc, &issued_error); } +// Return the boolean constant value, if it has one. + +bool +Binary_expression::do_boolean_constant_value(bool* val) const +{ + bool is_comparison = false; + switch (this->op_) + { + case OPERATOR_EQEQ: + case OPERATOR_NOTEQ: + case OPERATOR_LT: + case OPERATOR_LE: + case OPERATOR_GT: + case OPERATOR_GE: + is_comparison = true; + break; + case OPERATOR_ANDAND: + case OPERATOR_OROR: + break; + default: + return false; + } + + Numeric_constant left_nc, right_nc; + if (is_comparison + && this->left_->numeric_constant_value(&left_nc) + && this->right_->numeric_constant_value(&right_nc)) + return Binary_expression::compare_constant(this->op_, &left_nc, + &right_nc, + this->location(), + val); + + std::string left_str, right_str; + if (is_comparison + && this->left_->string_constant_value(&left_str) + && this->right_->string_constant_value(&right_str)) + { + *val = Binary_expression::cmp_to_bool(this->op_, + left_str.compare(right_str)); + return true; + } + + bool left_bval; + if (this->left_->boolean_constant_value(&left_bval)) + { + if (this->op_ == OPERATOR_ANDAND && !left_bval) + { + *val = false; + return true; + } + else if (this->op_ == OPERATOR_OROR && left_bval) + { + *val = true; + return true; + } + + bool right_bval; + if (this->right_->boolean_constant_value(&right_bval)) + { + switch (this->op_) + { + case OPERATOR_EQEQ: + *val = (left_bval == right_bval); + return true; + case OPERATOR_NOTEQ: + *val = (left_bval != right_bval); + return true; + case OPERATOR_ANDAND: + case OPERATOR_OROR: + *val = right_bval; + return true; + default: + go_unreachable(); + } + } + } + + return false; +} + // Note that the value is being discarded. bool diff --git a/gcc/go/gofrontend/expressions.h b/gcc/go/gofrontend/expressions.h index 09c71adbd31..6ba7fe1ee7c 100644 --- a/gcc/go/gofrontend/expressions.h +++ b/gcc/go/gofrontend/expressions.h @@ -581,6 +581,12 @@ class Expression string_constant_value(std::string* val) const { return this->do_string_constant_value(val); } + // If this is not a constant expression with boolean type, return + // false. If it is one, return true, and set VAL to the value. + bool + boolean_constant_value(bool* val) const + { return this->do_boolean_constant_value(val); } + // This is called if the value of this expression is being // discarded. This issues warnings about computed values being // unused. This returns true if all is well, false if it issued an @@ -1125,6 +1131,12 @@ class Expression do_string_constant_value(std::string*) const { return false; } + // Return whether this is a constant expression of boolean type, and + // set VAL to the value. + virtual bool + do_boolean_constant_value(bool*) const + { return false; } + // Called by the parser if the value is being discarded. virtual bool do_discarding_value(); @@ -1771,6 +1783,9 @@ class Type_conversion_expression : public Expression bool do_string_constant_value(std::string*) const; + bool + do_boolean_constant_value(bool*) const; + Type* do_type() { return this->type_; } @@ -1965,6 +1980,9 @@ class Unary_expression : public Expression bool do_numeric_constant_value(Numeric_constant*) const; + bool + do_boolean_constant_value(bool*) const; + Type* do_type(); @@ -2119,6 +2137,9 @@ class Binary_expression : public Expression bool do_numeric_constant_value(Numeric_constant*) const; + bool + do_boolean_constant_value(bool*) const; + bool do_discarding_value(); diff --git a/gcc/go/gofrontend/go.cc b/gcc/go/gofrontend/go.cc index 5106e8d0434..23acaa54f56 100644 --- a/gcc/go/gofrontend/go.cc +++ b/gcc/go/gofrontend/go.cc @@ -142,6 +142,9 @@ go_parse_input_files(const char** filenames, unsigned int filename_count, if (only_check_syntax) return; + // Do simple deadcode elimination. + ::gogo->remove_deadcode(); + // Make implicit type conversions explicit. ::gogo->add_conversions(); diff --git a/gcc/go/gofrontend/gogo.cc b/gcc/go/gofrontend/gogo.cc index 6e8ccbba226..480077823d1 100644 --- a/gcc/go/gofrontend/gogo.cc +++ b/gcc/go/gofrontend/gogo.cc @@ -3196,6 +3196,80 @@ Gogo::add_conversions_in_block(Block *b) b->traverse(&add_conversions); } +// Traversal class for simple deadcode elimination. + +class Remove_deadcode : public Traverse +{ + public: + Remove_deadcode() + : Traverse(traverse_statements + | traverse_expressions) + { } + + int + statement(Block*, size_t* pindex, Statement*); + + int + expression(Expression**); +}; + +// Remove deadcode in a statement. + +int +Remove_deadcode::statement(Block* block, size_t* pindex, Statement* sorig) +{ + Location loc = sorig->location(); + If_statement* ifs = sorig->if_statement(); + if (ifs != NULL) + { + // Remove the dead branch of an if statement. + bool bval; + if (ifs->condition()->boolean_constant_value(&bval)) + { + Statement* s; + if (bval) + s = Statement::make_block_statement(ifs->then_block(), + loc); + else + if (ifs->else_block() != NULL) + s = Statement::make_block_statement(ifs->else_block(), + loc); + else + // Make a dummy statement. + s = Statement::make_statement(Expression::make_boolean(false, loc), + true); + + block->replace_statement(*pindex, s); + } + } + return TRAVERSE_CONTINUE; +} + +// Remove deadcode in an expression. + +int +Remove_deadcode::expression(Expression** pexpr) +{ + // Discard the right arm of a shortcut expression of constant value. + Binary_expression* be = (*pexpr)->binary_expression(); + bool bval; + if (be != NULL + && be->boolean_constant_value(&bval) + && (be->op() == OPERATOR_ANDAND + || be->op() == OPERATOR_OROR)) + *pexpr = Expression::make_boolean(bval, be->location()); + return TRAVERSE_CONTINUE; +} + +// Remove deadcode. + +void +Gogo::remove_deadcode() +{ + Remove_deadcode remove_deadcode; + this->traverse(&remove_deadcode); +} + // Traverse the tree to create function descriptors as needed. class Create_function_descriptors : public Traverse diff --git a/gcc/go/gofrontend/gogo.h b/gcc/go/gofrontend/gogo.h index 11974764c4e..7f0cfc52ae7 100644 --- a/gcc/go/gofrontend/gogo.h +++ b/gcc/go/gofrontend/gogo.h @@ -688,6 +688,10 @@ class Gogo void check_return_statements(); + // Remove deadcode. + void + remove_deadcode(); + // Make implicit type conversions explicit. void add_conversions(); diff --git a/gcc/go/gofrontend/statements.h b/gcc/go/gofrontend/statements.h index bb715016433..3743d44bb43 100644 --- a/gcc/go/gofrontend/statements.h +++ b/gcc/go/gofrontend/statements.h @@ -1521,6 +1521,14 @@ class If_statement : public Statement condition() const { return this->cond_; } + Block* + then_block() const + { return this->then_block_; } + + Block* + else_block() const + { return this->else_block_; } + protected: int do_traverse(Traverse*); -- 2.30.2