aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--AGENTS/CONVENTIONS/TYPES.md1
-rw-r--r--doc/language/keyword/cpsc-keyword-and.7.md2
-rw-r--r--doc/language/keyword/cpsc-keyword-or.7.md2
-rw-r--r--include/cps/CodeGen.h2
-rw-r--r--lib/CodeGen/ExpressionGen.cc43
5 files changed, 48 insertions, 2 deletions
diff --git a/AGENTS/CONVENTIONS/TYPES.md b/AGENTS/CONVENTIONS/TYPES.md
index f5764d4..7349ab1 100644
--- a/AGENTS/CONVENTIONS/TYPES.md
+++ b/AGENTS/CONVENTIONS/TYPES.md
@@ -30,6 +30,7 @@ rejects arithmetic on BOOLEAN/CHAR, AND/OR on non-BOOLEAN, and comparisons betwe
different kinds (INTEGER with REAL is the only mixed pair); `emitCoercedExpr` allows
only INTEGER->REAL between the four builtin scalars. REAL->INTEGER therefore errors (with an `INT(...)` hint); `INT` is a compiler builtin (`Builtins.h`, `CodeGen::emitIntBuiltin`) truncating toward zero with a range check. BOOLEAN (i1) and CHAR (i8)
compare unsigned. C scalars keep C semantics.
+AND/OR short-circuit (`CodeGen::emitShortCircuit`) unless a user overload applies.
**User-kind typing is nominal, and checked upstream only.** Enum/Record/Pointer
identity is case-insensitive name equality (`P1 = ^INTEGER` and `P2 = ^INTEGER` are
diff --git a/doc/language/keyword/cpsc-keyword-and.7.md b/doc/language/keyword/cpsc-keyword-and.7.md
index 7da90b7..52b0a9a 100644
--- a/doc/language/keyword/cpsc-keyword-and.7.md
+++ b/doc/language/keyword/cpsc-keyword-and.7.md
@@ -12,7 +12,7 @@ left AND right
# DESCRIPTION
-Return BOOLEAN: true when both operands are true. Both operands must be BOOLEAN; numbers and characters are rejected. Both operands are evaluated; AND does not short-circuit. AND binds more tightly than OR and less tightly than comparisons. Use parentheses for clarity.
+Return BOOLEAN: true when both operands are true. Both operands must be BOOLEAN; numbers and characters are rejected. AND short-circuits: the right operand is evaluated only when the left one is true. AND binds more tightly than OR and less tightly than comparisons. Use parentheses for clarity.
# EXAMPLE
diff --git a/doc/language/keyword/cpsc-keyword-or.7.md b/doc/language/keyword/cpsc-keyword-or.7.md
index 678ff19..986044e 100644
--- a/doc/language/keyword/cpsc-keyword-or.7.md
+++ b/doc/language/keyword/cpsc-keyword-or.7.md
@@ -12,7 +12,7 @@ left OR right
# DESCRIPTION
-Return BOOLEAN: true when either operand is true. Both operands must be BOOLEAN; numbers and characters are rejected. Both operands are evaluated; OR does not short-circuit. OR binds less tightly than AND and comparisons.
+Return BOOLEAN: true when either operand is true. Both operands must be BOOLEAN; numbers and characters are rejected. OR short-circuits: the right operand is evaluated only when the left one is false. OR binds less tightly than AND and comparisons.
# EXAMPLE
diff --git a/include/cps/CodeGen.h b/include/cps/CodeGen.h
index 3b0296f..539a6e6 100644
--- a/include/cps/CodeGen.h
+++ b/include/cps/CodeGen.h
@@ -201,6 +201,8 @@ class CodeGen {
// Read a value from resolved storage.
llvm::Value *loadFromLValue(const LValueInfo &LV);
llvm::Value *emitAllocationSize(ExprAST *Expr);
+ static bool isLogicalOperand(const TypeInfo *T);
+ llvm::Value *emitShortCircuit(BinaryExprAST *Bin, const TypeInfo *LT, const TypeInfo *RT);
llvm::Value *emitIntBuiltin(ExprAST *Arg, int Line);
llvm::Value *emitCall(const std::string &Callee,
const std::vector<std::unique_ptr<ExprAST>> &ArgExprs, int Line,
diff --git a/lib/CodeGen/ExpressionGen.cc b/lib/CodeGen/ExpressionGen.cc
index 51d7087..45583d3 100644
--- a/lib/CodeGen/ExpressionGen.cc
+++ b/lib/CodeGen/ExpressionGen.cc
@@ -145,6 +145,9 @@ Value *CodeGen::emitExpr(ExprAST *Expr) {
if (Overload.Signature)
return emitOperatorCall(*Overload.Signature, Bin->getLHS(), Bin->getRHS());
}
+ if ((Bin->getOp() == tok_and || Bin->getOp() == tok_or) && LT && RT &&
+ Builder->GetInsertBlock() && isLogicalOperand(LT) && isLogicalOperand(RT))
+ return emitShortCircuit(Bin, LT, RT);
Value *L = emitExpr(Bin->getLHS());
Value *R = emitExpr(Bin->getRHS());
return emitBinaryOp(Bin->getOp(), L, LT, R, RT, Bin->getLine());
@@ -173,6 +176,46 @@ Value *CodeGen::emitExpr(ExprAST *Expr) {
return nullptr;
}
+// BOOLEAN, or a C scalar tested against zero; everything else keeps the
+// ordinary operator path (and its diagnostics).
+bool CodeGen::isLogicalOperand(const TypeInfo *T) {
+ return T->Kind == TypeKind::Boolean || (T->Kind == TypeKind::CScalar && !T->isVoid());
+}
+
+// AND / OR evaluate the right operand only when the left one does not
+// already decide the result.
+Value *CodeGen::emitShortCircuit(BinaryExprAST *Bin, const TypeInfo *LT, const TypeInfo *RT) {
+ SaveAndRestore<int> Location(DiagnosticLine, Bin->getLine() ? Bin->getLine() : DiagnosticLine);
+ const TypeInfo *Bool = resolveType("BOOLEAN");
+ bool IsAnd = Bin->getOp() == tok_and;
+ auto EmitOperand = [&](ExprAST *E, const TypeInfo *T) -> Value * {
+ Value *V = emitExpr(E);
+ if (!V) return nullptr;
+ return T->Kind == TypeKind::Boolean ? V : convertNumericValue(V, T, Bool);
+ };
+
+ Value *L = EmitOperand(Bin->getLHS(), LT);
+ if (!L) return nullptr;
+ Function *TheFunction = Builder->GetInsertBlock()->getParent();
+ BasicBlock *LeftBB = Builder->GetInsertBlock();
+ BasicBlock *RightBB = BasicBlock::Create(*TheContext, IsAnd ? "and_rhs" : "or_rhs", TheFunction);
+ BasicBlock *DoneBB = BasicBlock::Create(*TheContext, IsAnd ? "and_done" : "or_done", TheFunction);
+ if (IsAnd) Builder->CreateCondBr(L, RightBB, DoneBB);
+ else Builder->CreateCondBr(L, DoneBB, RightBB);
+
+ Builder->SetInsertPoint(RightBB);
+ Value *R = EmitOperand(Bin->getRHS(), RT);
+ if (!R) return nullptr;
+ BasicBlock *RightEndBB = Builder->GetInsertBlock();
+ Builder->CreateBr(DoneBB);
+
+ Builder->SetInsertPoint(DoneBB);
+ PHINode *Result = Builder->CreatePHI(Type::getInt1Ty(*TheContext), 2, IsAnd ? "andtmp" : "ortmp");
+ Result->addIncoming(ConstantInt::get(Type::getInt1Ty(*TheContext), IsAnd ? 0 : 1), LeftBB);
+ Result->addIncoming(R, RightEndBB);
+ return Result;
+}
+
// Ordinary binary expressions and CASE use the same typed operations.
Value *CodeGen::emitBinaryOp(int Op, Value *L, const TypeInfo *LT,
Value *R, const TypeInfo *RT, int Line) {