ExpressionPtr OrExpression::getDNFImpl() const { UnaryExpression * leftLeaf = dynamic_cast<UnaryExpression*>(left()); UnaryExpression * rightLeaf = dynamic_cast<UnaryExpression*>(right()); // A or B if (leftLeaf && rightLeaf) { return clone(); } // A or (B and C) => (A and B) or (A and C) BinaryExpression * rightAnd = dynamic_cast<AndExpression*>(right()); if (leftLeaf && rightAnd) { return Or(And(leftLeaf, rightAnd->left()), And(leftLeaf, rightAnd->right())); } // (A and B) or C => (A and C) or (B and C) BinaryExpression * leftAnd = dynamic_cast<AndExpression*>(left()); if (leftAnd && rightLeaf) { return Or(And(leftAnd->left(), rightLeaf), And(leftAnd->right(), rightLeaf)); } return Or(left()->getDNF(), right()->getDNF()); }
// visitForTest helps optimize statements with expressions like: // if (x && y || z) // // Where, with a naive conversion we would reduce (x && y) to a boolean, and // then reduce (that || z) to another boolean, and another test - we can simply // test each individual component and combine the jump paths with the paths // the if-test needs to take. // // Unfortunately, this logic comes at a complexity price: it is illegal to // emit non-statements from SemA, since the following situation could occur: // (1) SemA builds HIR chain #1 for statement X. // (2) SemA builds HIR chain #2 for statement X. // (3) SemA forces HIR chain #2 to be emitted to the code stream. // (4) SemA emits statement X, with HIR chain #1. // // In this situation, the HIR chains have been emitted out-of-order. So, // visitForTest takes in a HIRList which it populates with instructions, which // may include internal statements (like jumps and binds). // // The trueBranch and falseBranch parameters are self-explanatory. The // fallthrough case describes which branch comes immediately after the // test. This is used to optimize cases like: // if (x) { // ... // } // do { // ... // } while (y); // // After testing |x|, if it evaluated to true, there is no need to jump to the // true branch, because we'll fallthrough (as long as the expression does not // have logical ands/ors which could short-circuit). Similarly for |y|, the // false condition does not need an actual jump target. // void SemanticAnalysis::visitForTest(HIRList *output, Expression *expr, Label *trueBranch, Label *falseBranch, Label *fallthrough) { Type *boolType = cc_.types()->getPrimitive(PrimitiveType::Bool); // Handle logical and/or. BinaryExpression *bin = expr->asBinaryExpression(); if (bin && (bin->token() == TOK_AND || bin->token() == TOK_OR)) { HLabel *next = new (pool_) HLabel(); if (bin->token() == TOK_AND) visitForTest(output, bin->left(), next, falseBranch, next); else visitForTest(output, bin->left(), trueBranch, next, next); output->append(new (pool_) HBind(expr, next)); visitForTest(output, bin->right(), trueBranch, falseBranch, fallthrough); return; } // Handle equality and relational operators. if (bin && (bin->token() >= TOK_EQUALS && bin->token() <= TOK_GE)) { HIR *left = rvalue(bin->left()); HIR *right = rvalue(bin->right()); if (!left || !right) return; Type *coercion = coercionType(bin->token(), left, right); if (!coercion || ((left = coerce(left, coercion, Coerce_Arg)) == nullptr) || ((right = coerce(right, coercion, Coerce_Arg)) == nullptr)) { return; } Label *target = (fallthrough == trueBranch) ? falseBranch : trueBranch; TokenKind token = (fallthrough == trueBranch) ? InvertTest(bin->token()) : bin->token(); output->append(new (pool_) HCompareAndJump(bin, token, left, right, target)); return; } // Handle unary not (!) UnaryExpression *unary = expr->asUnaryExpression(); if (unary && unary->token() == TOK_NOT) { // Re-invoke visitForTest but invert the branches. Note that we don't touch // |fallthrough|, since it's used to determine whether to jump on success // or failure, so inverting it as well would do nothing. visitForTest(output, unary->expression(), falseBranch, trueBranch, fallthrough); return; } // We couldn't match anything easy, so just coerce the input to a boolean. HIR *hir = rvalue(expr); if (!hir || ((hir = coerce(hir, boolType, Coerce_Assign)) == nullptr)) return; if (fallthrough == falseBranch) output->append(new (pool_) HJump(expr, hir, true, trueBranch)); else output->append(new (pool_) HJump(expr, hir, false, falseBranch)); }