// See the file "COPYING" in the main distribution directory for copyright. #include "pac_expr.h" #include "pac_case.h" #include "pac_cstr.h" #include "pac_exception.h" #include "pac_exttype.h" #include "pac_id.h" #include "pac_let.h" #include "pac_nullptr.h" #include "pac_number.h" #include "pac_output.h" #include "pac_record.h" #include "pac_regex.h" #include "pac_typedecl.h" #include "pac_utils.h" string OrigExprList(ExprList* list) { bool first = true; string str; foreach (i, ExprList, list) { Expr* expr = *i; if ( first ) first = false; else str += ", "; str += expr->orig(); } return str; } string EvalExprList(ExprList* exprlist, Output* out, Env* env) { string val_list(""); bool first = true; foreach (i, ExprList, exprlist) { if ( ! first ) val_list += ", "; val_list += (*i)->EvalExpr(out, env); first = false; } return val_list; } static const char* expr_fmt[] = { // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) #define EXPR_DEF(type, num_op, fmt) fmt, #include "pac_expr.def" #undef EXPR_DEF }; void Expr::init() { id_ = nullptr; num_ = nullptr; cstr_ = nullptr; regex_ = nullptr; num_operands_ = 0; operand_[0] = nullptr; operand_[1] = nullptr; operand_[2] = nullptr; args_ = nullptr; cases_ = nullptr; } Expr::Expr(ID* arg_id) : DataDepElement(EXPR) { init(); expr_type_ = EXPR_ID; id_ = arg_id; orig_ = strfmt("%s", id_->Name()); } Expr::Expr(Number* arg_num) : DataDepElement(EXPR) { init(); expr_type_ = EXPR_NUM; num_ = arg_num; orig_ = strfmt("((int) %s)", num_->Str()); } Expr::Expr(Nullptr* arg_nullp) : DataDepElement(EXPR) { init(); expr_type_ = EXPR_NULLPTR; nullp_ = arg_nullp; orig_ = strfmt("%s", nullp_->Str()); } Expr::Expr(ConstString* cstr) : DataDepElement(EXPR) { init(); expr_type_ = EXPR_CSTR; cstr_ = cstr; orig_ = cstr_->str(); } Expr::Expr(RegEx* regex) : DataDepElement(EXPR) { init(); expr_type_ = EXPR_REGEX; regex_ = regex; orig_ = strfmt("/%s/", regex_->str().c_str()); } Expr::Expr(ExprType arg_type, Expr* op1) : DataDepElement(EXPR) { init(); expr_type_ = arg_type; num_operands_ = 1; operand_[0] = op1; orig_ = strfmt(expr_fmt[expr_type_], op1->orig()); } Expr::Expr(ExprType arg_type, Expr* op1, Expr* op2) : DataDepElement(EXPR) { init(); expr_type_ = arg_type; num_operands_ = 2; operand_[0] = op1; operand_[1] = op2; operand_[2] = nullptr; orig_ = strfmt(expr_fmt[expr_type_], op1->orig(), op2->orig()); } Expr::Expr(ExprType arg_type, Expr* op1, Expr* op2, Expr* op3) : DataDepElement(EXPR) { init(); expr_type_ = arg_type; num_operands_ = 3; operand_[0] = op1; operand_[1] = op2; operand_[2] = op3; orig_ = strfmt(expr_fmt[expr_type_], op1->orig(), op2->orig(), op3->orig()); } Expr::Expr(ExprList* args) : DataDepElement(EXPR) { init(); expr_type_ = EXPR_CALLARGS; num_operands_ = -1; args_ = args; orig_ = OrigExprList(args_); } Expr::Expr(Expr* index, CaseExprList* cases) : DataDepElement(EXPR) { init(); expr_type_ = EXPR_CASE; num_operands_ = -1; operand_[0] = index; cases_ = cases; orig_ = strfmt("case %s of { ", index->orig()); foreach (i, CaseExprList, cases_) { CaseExpr* c = *i; orig_ += strfmt("%s => %s; ", OrigExprList(c->index()).c_str(), c->value()->orig()); } orig_ += "}"; } Expr::~Expr() { delete id_; delete operand_[0]; delete operand_[1]; delete operand_[2]; delete_list(args_); delete_list(cases_); } void Expr::AddCaseExpr(CaseExpr* case_expr) { ASSERT(str_.empty()); ASSERT(expr_type_ == EXPR_CASE); ASSERT(cases_); cases_->push_back(case_expr); } void Expr::GenStrFromFormat(Env* env) { // The format != "@custom@" ASSERT(*expr_fmt[expr_type_] != '@'); switch ( num_operands_ ) { case 1: str_ = strfmt(expr_fmt[expr_type_], operand_[0]->str()); break; case 2: str_ = strfmt(expr_fmt[expr_type_], operand_[0]->str(), operand_[1]->str()); break; case 3: str_ = strfmt(expr_fmt[expr_type_], operand_[0]->str(), operand_[1]->str(), operand_[2]->str()); break; default: DEBUG_MSG("num_operands_ = %d, orig = %s\n", num_operands_, orig()); ASSERT(0); break; } } namespace { RecordField* GetRecordField(const ID* id, Env* env) { Field* field = env->GetField(id); ASSERT(field); if ( field->tof() != RECORD_FIELD && field->tof() != PADDING_FIELD ) throw Exception(id, "not a record field"); RecordField* r = static_cast(field); ASSERT(r); return r; } } // namespace void Expr::GenCaseEval(Output* out_cc, Env* env) { ASSERT(expr_type_ == EXPR_CASE); ASSERT(operand_[0]); ASSERT(cases_); Type* val_type = DataType(env); ID* val_var = env->AddTempID(val_type); // DataType(env) can return a null pointer if an enum value is not // defined. if ( ! val_type ) throw Exception(this, "undefined case value"); out_cc->println("%s %s;", val_type->DataTypeStr().c_str(), env->LValue(val_var)); // force evaluation of IDs appearing in case stmt operand_[0]->ForceIDEval(out_cc, env); foreach (i, CaseExprList, cases_) (*i)->value()->ForceIDEval(out_cc, env); out_cc->println("// NOLINTBEGIN(bugprone-branch-clone)"); out_cc->println("switch ( %s ) {", operand_[0]->EvalExpr(out_cc, env)); Type* switch_type = operand_[0]->DataType(env); out_cc->inc_indent(); CaseExpr* default_case = nullptr; foreach (i, CaseExprList, cases_) { CaseExpr* c = *i; ExprList* index = c->index(); if ( ! index ) { if ( default_case ) throw Exception(c, "duplicate default cases"); default_case = c; } else { GenCaseStr(index, out_cc, env, switch_type); out_cc->inc_indent(); out_cc->println("%s = %s;", env->LValue(val_var), c->value()->EvalExpr(out_cc, env)); out_cc->println("break;"); out_cc->dec_indent(); } } // Generate the default case after all other cases GenCaseStr(nullptr, out_cc, env, switch_type); out_cc->inc_indent(); if ( default_case ) { out_cc->println("%s = %s;", env->LValue(val_var), default_case->value()->EvalExpr(out_cc, env)); } else { out_cc->println("throw binpac::ExceptionInvalidCaseIndex(\"%s\", (int64)%s);", Location(), operand_[0]->EvalExpr(out_cc, env)); } out_cc->println("break;"); out_cc->dec_indent(); out_cc->dec_indent(); out_cc->println("}"); out_cc->println("// NOLINTEND(bugprone-branch-clone)"); env->SetEvaluated(val_var); str_ = env->RValue(val_var); } void Expr::GenEval(Output* out_cc, Env* env) { switch ( expr_type_ ) { case EXPR_NUM: str_ = num_->Str(); break; case EXPR_NULLPTR: str_ = nullp_->Str(); break; case EXPR_ID: if ( ! env->Evaluated(id_) ) env->Evaluate(out_cc, id_); str_ = env->RValue(id_); break; case EXPR_MEMBER: { /* For member expressions such X.Y, evaluating X only is sufficient. (Actually trying to evaluate Y will lead to error because Y is not defined in the current environment.) */ operand_[0]->GenEval(out_cc, env); Type* ty0 = operand_[0]->DataType(env); if ( ty0 ) { str_ = strfmt("%s%s", operand_[0]->EvalExpr(out_cc, env), ty0->EvalMember(operand_[1]->id()).c_str()); } else { string tmp = strfmt("->%s()", operand_[1]->id()->Name()); str_ = strfmt("%s%s", operand_[0]->EvalExpr(out_cc, env), tmp.c_str()); } } break; case EXPR_SUBSCRIPT: { operand_[0]->GenEval(out_cc, env); operand_[1]->GenEval(out_cc, env); string v0 = operand_[0]->EvalExpr(out_cc, env); string v1 = operand_[1]->EvalExpr(out_cc, env); Type* ty0 = operand_[0]->DataType(env); if ( ty0 ) str_ = ty0->EvalElement(v0, v1); else str_ = strfmt("%s[%s]", v0.c_str(), v1.c_str()); } break; case EXPR_SIZEOF: { const ID* id = operand_[0]->id(); RecordField* rf; Type* ty; try { if ( rf = GetRecordField(id, env); rf != nullptr ) { str_ = strfmt("%s", rf->FieldSize(out_cc, env)); } } catch ( ExceptionIDNotFound& e ) { if ( ty = TypeDecl::LookUpType(id); ty != nullptr ) { int ty_size = ty->StaticSize(global_env()); if ( ty_size >= 0 ) str_ = strfmt("%d", ty_size); else throw Exception(id, "unknown size"); } else throw Exception(id, "not a record field or type"); } } break; case EXPR_OFFSETOF: { const ID* id = operand_[0]->id(); RecordField* rf = GetRecordField(id, env); str_ = strfmt("%s", rf->FieldOffset(out_cc, env)); } break; case EXPR_CALLARGS: str_ = EvalExprList(args_, out_cc, env); break; case EXPR_CASE: GenCaseEval(out_cc, env); break; default: // Evaluate every operand by default for ( auto& op : operand_ ) if ( op ) op->GenEval(out_cc, env); GenStrFromFormat(env); break; } } void Expr::ForceIDEval(Output* out_cc, Env* env) { switch ( expr_type_ ) { case EXPR_NUM: case EXPR_SIZEOF: case EXPR_OFFSETOF: break; case EXPR_ID: if ( ! env->Evaluated(id_) ) env->Evaluate(out_cc, id_); break; case EXPR_MEMBER: operand_[0]->ForceIDEval(out_cc, env); break; case EXPR_CALLARGS: { foreach (i, ExprList, args_) (*i)->ForceIDEval(out_cc, env); } break; case EXPR_CASE: { operand_[0]->ForceIDEval(out_cc, env); foreach (i, CaseExprList, cases_) (*i)->value()->ForceIDEval(out_cc, env); } break; default: // Evaluate every operand by default for ( auto& op : operand_ ) if ( op ) op->ForceIDEval(out_cc, env); break; } } const char* Expr::EvalExpr(Output* out_cc, Env* env) { GenEval(out_cc, env); return str(); } Type* Expr::DataType(Env* env) const { Type* data_type; switch ( expr_type_ ) { case EXPR_ID: data_type = env->GetDataType(id_); break; case EXPR_MEMBER: { // Get type of the parent Type* parent_type = operand_[0]->DataType(env); if ( ! parent_type ) return nullptr; data_type = parent_type->MemberDataType(operand_[1]->id()); } break; case EXPR_SUBSCRIPT: { // Get type of the parent Type* parent_type = operand_[0]->DataType(env); data_type = parent_type->ElementDataType(); } break; case EXPR_PAREN: data_type = operand_[0]->DataType(env); break; case EXPR_COND: { Type* type1 = operand_[1]->DataType(env); Type* type2 = operand_[2]->DataType(env); if ( ! Type::CompatibleTypes(type1, type2) ) { throw Exception(this, strfmt("type mismatch: %s vs %s", type1->DataTypeStr().c_str(), type2->DataTypeStr().c_str())); } data_type = type1; } break; case EXPR_CALL: data_type = operand_[0]->DataType(env); break; case EXPR_CASE: { if ( cases_ && ! cases_->empty() ) { Type* type1 = cases_->front()->value()->DataType(env); Type* numeric_with_largest_width = nullptr; foreach (i, CaseExprList, cases_) { Type* type2 = (*i)->value()->DataType(env); if ( ! Type::CompatibleTypes(type1, type2) ) { throw Exception(this, strfmt("type mismatch: %s vs %s", type1->DataTypeStr().c_str(), type2->DataTypeStr().c_str())); } if ( type1 == extern_type_nullptr ) type1 = type2; if ( type2 && type2->IsNumericType() ) { if ( numeric_with_largest_width ) { int largest; int contender; // External C++ types like "int", "bool", "enum" use "int" // storage internally. if ( numeric_with_largest_width->tot() == Type::EXTERN ) largest = sizeof(int); else largest = numeric_with_largest_width->StaticSize(env); if ( type2->tot() == Type::EXTERN ) contender = sizeof(int); else contender = type2->StaticSize(env); if ( contender > largest ) numeric_with_largest_width = type2; } else numeric_with_largest_width = type2; } } data_type = numeric_with_largest_width ? numeric_with_largest_width : type1; } else data_type = nullptr; } break; case EXPR_NUM: case EXPR_SIZEOF: case EXPR_OFFSETOF: case EXPR_NEG: case EXPR_PLUS: case EXPR_MINUS: case EXPR_TIMES: case EXPR_DIV: case EXPR_MOD: case EXPR_BITNOT: case EXPR_BITAND: case EXPR_BITOR: case EXPR_BITXOR: case EXPR_LSHIFT: case EXPR_RSHIFT: case EXPR_EQUAL: case EXPR_GE: case EXPR_LE: case EXPR_GT: case EXPR_LT: case EXPR_NOT: case EXPR_AND: case EXPR_OR: data_type = extern_type_int; break; default: data_type = nullptr; break; } return data_type; } string Expr::DataTypeStr(Env* env) const { Type* type = DataType(env); if ( ! type ) { throw Exception(this, strfmt("cannot find data type for expression `%s'", orig())); } return type->DataTypeStr(); } string Expr::SetFunc(Output* out, Env* env) { switch ( expr_type_ ) { case EXPR_ID: return set_function(id_); case EXPR_MEMBER: { // Evaluate the parent string parent_val(operand_[0]->EvalExpr(out, env)); return parent_val + "->" + set_function(operand_[1]->id()); } break; default: throw Exception(this, strfmt("cannot generate set function " "for expression `%s'", orig())); break; } } bool Expr::ConstFold(Env* env, int* pn) const { switch ( expr_type_ ) { case EXPR_NUM: *pn = num_->Num(); return true; case EXPR_ID: return env->GetConstant(id_, pn); default: // ### FIXME: folding consts return false; } } // TODO: build a generic data dependency extraction process namespace { // Maximum of two minimal header sizes int mhs_max(int h1, int h2) { if ( h1 < 0 || h2 < 0 ) return -1; else { // return max(h1, h2); return h1 > h2 ? h1 : h2; } } // MHS required to evaluate the field int mhs_letfield(Env* env, LetField* field) { return field->expr()->MinimalHeaderSize(env); } int mhs_recordfield(Env* env, RecordField* field) { int offset = field->static_offset(); if ( offset < 0 ) // offset cannot be statically determined return -1; int size = field->StaticSize(env, offset); if ( size < 0 ) // size cannot be statically determined return -1; return offset + size; } int mhs_casefield(Env* env, CaseField* field) { // TODO: deal with the index int size = field->StaticSize(env); if ( size < 0 ) // size cannot be statically determined return -1; return size; } int mhs_field(Env* env, Field* field) { int mhs = -1; switch ( field->tof() ) { case LET_FIELD: { LetField* f = static_cast(field); ASSERT(f); mhs = mhs_letfield(env, f); } break; case CONTEXT_FIELD: case FLOW_FIELD: ASSERT(0); break; case PARAM_FIELD: mhs = 0; break; case RECORD_FIELD: case PADDING_FIELD: { RecordField* f = static_cast(field); ASSERT(f); mhs = mhs_recordfield(env, f); } break; case CASE_FIELD: { CaseField* f = static_cast(field); ASSERT(f); mhs = mhs_casefield(env, f); } break; case PARSE_VAR_FIELD: case PRIV_VAR_FIELD: case PUB_VAR_FIELD: case TEMP_VAR_FIELD: mhs = 0; break; case WITHINPUT_FIELD: { // ### TODO: fix this mhs = -1; } break; } return mhs; } int mhs_id(Env* env, const ID* id) { int mhs = -1; switch ( env->GetIDType(id) ) { case CONST: case GLOBAL_VAR: case TEMP_VAR: case STATE_VAR: case FUNC_ID: case FUNC_PARAM: mhs = 0; break; case MEMBER_VAR: case PRIV_MEMBER_VAR: { Field* field = env->GetField(id); if ( ! field ) throw ExceptionIDNotField(id); mhs = mhs_field(env, field); } break; case UNION_VAR: // TODO: deal with UNION_VAR mhs = -1; break; case MACRO: { Expr* e = env->GetMacro(id); mhs = e->MinimalHeaderSize(env); } break; } return mhs; } } // namespace int Expr::MinimalHeaderSize(Env* env) { int mhs; switch ( expr_type_ ) { case EXPR_NUM: // Zero byte is required mhs = 0; break; case EXPR_ID: mhs = mhs_id(env, id_); break; case EXPR_MEMBER: // TODO: this is not a tight bound because // one actually does not have to parse the // whole record to compute one particular // field. mhs = operand_[0]->MinimalHeaderSize(env); break; case EXPR_SUBSCRIPT: { int index; Type* array_type = operand_[0]->DataType(env); Type* elem_type = array_type->ElementDataType(); int elem_size = elem_type->StaticSize(env); if ( elem_size >= 0 && operand_[1]->ConstFold(env, &index) ) { mhs = elem_size * index; } else { mhs = -1; } } break; case EXPR_SIZEOF: { const ID* id = operand_[0]->id(); ASSERT(id); RecordField* rf; Type* ty; if ( rf = GetRecordField(id, env); rf != nullptr ) { if ( rf->StaticSize(env, -1) >= 0 ) mhs = 0; else mhs = mhs_recordfield(env, rf); } else if ( ty = TypeDecl::LookUpType(id); ty != nullptr ) { mhs = 0; } else throw Exception(id, "not a record field or type"); } break; case EXPR_OFFSETOF: { const ID* id = operand_[0]->id(); ASSERT(id); RecordField* field = GetRecordField(id, env); mhs = field->static_offset(); if ( mhs < 0 ) { mhs = 0; // Take the MHS of the preceding (non-let) field RecordField* prev_field = field->prev(); ASSERT(prev_field); mhs = mhs_recordfield(env, prev_field); } } break; case EXPR_CALLARGS: { mhs = 0; if ( args_ ) for ( const auto& arg : *args_ ) mhs = mhs_max(mhs, arg->MinimalHeaderSize(env)); } break; case EXPR_CASE: { mhs = operand_[0]->MinimalHeaderSize(env); for ( const auto& ce : *cases_ ) { if ( ce->index() ) for ( const auto& idx : *(ce->index()) ) mhs = mhs_max(mhs, idx->MinimalHeaderSize(env)); mhs = mhs_max(mhs, ce->value()->MinimalHeaderSize(env)); } } break; default: // Evaluate every operand by default mhs = 0; for ( auto& op : operand_ ) if ( op ) mhs = mhs_max(mhs, op->MinimalHeaderSize(env)); break; } return mhs; } bool Expr::HasReference(const ID* id) const { switch ( expr_type_ ) { case EXPR_ID: return *id == *id_; case EXPR_MEMBER: return operand_[0]->HasReference(id); case EXPR_CALLARGS: { foreach (i, ExprList, args_) if ( (*i)->HasReference(id) ) return true; } return false; case EXPR_CASE: { foreach (i, CaseExprList, cases_) if ( (*i)->HasReference(id) ) return true; } return false; default: // Evaluate every operand by default for ( auto& op : operand_ ) { if ( op && op->HasReference(id) ) { return true; } } return false; } } bool Expr::DoTraverse(DataDepVisitor* visitor) { switch ( expr_type_ ) { case EXPR_ID: break; case EXPR_MEMBER: /* For member expressions such X.Y, evaluating X only is sufficient. (Actually trying to evaluate Y will lead to error because Y is not defined in the current environment.) */ if ( ! operand_[0]->Traverse(visitor) ) return false; break; case EXPR_CALLARGS: { foreach (i, ExprList, args_) if ( ! (*i)->Traverse(visitor) ) return false; } break; case EXPR_CASE: { foreach (i, CaseExprList, cases_) if ( ! (*i)->Traverse(visitor) ) return false; } break; default: // Evaluate every operand by default for ( auto& op : operand_ ) { if ( op && ! op->Traverse(visitor) ) { return false; } } break; } return true; } bool Expr::RequiresAnalyzerContext() const { switch ( expr_type_ ) { case EXPR_ID: return *id_ == *analyzer_context_id; case EXPR_MEMBER: /* For member expressions such X.Y, evaluating X only is sufficient. (Actually trying to evaluate Y will lead to error because Y is not defined in the current environment.) */ return operand_[0]->RequiresAnalyzerContext(); case EXPR_CALLARGS: { foreach (i, ExprList, args_) if ( (*i)->RequiresAnalyzerContext() ) return true; } return false; case EXPR_CASE: { foreach (i, CaseExprList, cases_) if ( (*i)->RequiresAnalyzerContext() ) return true; } return false; default: // Evaluate every operand by default for ( auto& op : operand_ ) if ( op && op->RequiresAnalyzerContext() ) { DEBUG_MSG("'%s' requires analyzer context\n", op->orig()); return true; } return false; } } CaseExpr::CaseExpr(ExprList* index, Expr* value) : DataDepElement(DataDepElement::CASEEXPR), index_(index), value_(value) {} CaseExpr::~CaseExpr() { delete_list(index_); index_ = nullptr; delete value_; } bool CaseExpr::DoTraverse(DataDepVisitor* visitor) { foreach (i, ExprList, index_) if ( ! (*i)->Traverse(visitor) ) return false; return value_->Traverse(visitor); } bool CaseExpr::HasReference(const ID* id) const { return value_->HasReference(id); } bool CaseExpr::RequiresAnalyzerContext() const { // index_ should evaluate to constants return value_->RequiresAnalyzerContext(); }