Skip to content

Commit

Permalink
Logical operators || and && are short-circuiting.asm
Browse files Browse the repository at this point in the history
`true || X` short-circuits to 1 without evaluating X
`false && X` short-circuits to 0 without evaluating X

Fixes gbdev#619
  • Loading branch information
Rangi42 committed Dec 26, 2020
1 parent c20ac35 commit 0fdd7ff
Show file tree
Hide file tree
Showing 7 changed files with 88 additions and 7 deletions.
3 changes: 3 additions & 0 deletions include/asm/rpn.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,9 @@ static inline bool rpn_isSymbol(const struct Expression *expr)
return expr->isSymbol;
}

void rpn_EnterShortCircuitOp(bool startShortCircuit);
void rpn_LeaveShortCircuitOp(void);
bool rpn_IsShortCircuited(void);
void rpn_Symbol(struct Expression *expr, char const *tzSym);
void rpn_Number(struct Expression *expr, uint32_t i);
void rpn_LOGNOT(struct Expression *expr, const struct Expression *src);
Expand Down
3 changes: 3 additions & 0 deletions src/asm/lexer.c
Original file line number Diff line number Diff line change
Expand Up @@ -1371,6 +1371,9 @@ static char const *readInterpolation(void)
}
}

if (rpn_IsShortCircuited())
return NULL;

if (i == sizeof(symName)) {
warning(WARNING_LONG_STR, "Symbol name too long\n");
i--;
Expand Down
35 changes: 28 additions & 7 deletions src/asm/parser.y
Original file line number Diff line number Diff line change
Expand Up @@ -939,11 +939,27 @@ relocexpr_no_str : scoped_anon_id { rpn_Symbol(&$$, $1); }
| T_OP_LOGICNOT relocexpr %prec NEG {
rpn_LOGNOT(&$$, &$2);
}
| relocexpr T_OP_LOGICOR relocexpr {
rpn_BinaryOp(RPN_LOGOR, &$$, &$1, &$3);
}
| relocexpr T_OP_LOGICAND relocexpr {
rpn_BinaryOp(RPN_LOGAND, &$$, &$1, &$3);
| relocexpr T_OP_LOGICOR {
// If the first operand is nonzero...
rpn_EnterShortCircuitOp(rpn_isKnown(&$1) && $1.nVal);
} relocexpr {
// ...then short-circuit to 1 without evaluating the second operand.
if (rpn_IsShortCircuited())
rpn_Number(&$$, 1);
else
rpn_BinaryOp(RPN_LOGOR, &$$, &$1, &$4);
rpn_LeaveShortCircuitOp();
}
| relocexpr T_OP_LOGICAND {
// If the first operand is zero...
rpn_EnterShortCircuitOp(rpn_isKnown(&$1) && !$1.nVal);
} relocexpr {
// ...then short-circuit to 0 without evaluating the second operand.
if (rpn_IsShortCircuited())
rpn_Number(&$$, 0);
else
rpn_BinaryOp(RPN_LOGAND, &$$, &$1, &$4);
rpn_LeaveShortCircuitOp();
}
| relocexpr T_OP_LOGICEQU relocexpr {
rpn_BinaryOp(RPN_LOGEQ, &$$, &$1, &$3);
Expand Down Expand Up @@ -1089,7 +1105,10 @@ const : relocexpr {

string : T_STRING
| T_OP_STRSUB T_LPAREN string T_COMMA uconst T_COMMA uconst T_RPAREN {
strsubUTF8($$, $3, $5, $7);
if (rpn_IsShortCircuited())
$$[0] = '\0';
else
strsubUTF8($$, $3, $5, $7);
}
| T_OP_STRCAT T_LPAREN T_RPAREN {
$$[0] = '\0';
Expand All @@ -1107,7 +1126,9 @@ string : T_STRING

strcat_args : string
| strcat_args T_COMMA string {
if (snprintf($$, MAXSTRLEN + 1, "%s%s", $1, $3) > MAXSTRLEN)
if (rpn_IsShortCircuited())
$$[0] = '\0';
else if (snprintf($$, MAXSTRLEN + 1, "%s%s", $1, $3) > MAXSTRLEN)
warning(WARNING_LONG_STR, "STRCAT: String too long '%s%s'\n",
$1, $3);
}
Expand Down
42 changes: 42 additions & 0 deletions src/asm/rpn.c
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@
#include "asm/symbol.h"
#include "asm/warning.h"

uint32_t nestedShortCircuitOps; // How many nested short-circuitable operators there currently are
uint32_t shortCircuitAtDepth; // The nested depth at which short-circuiting occurred (0 if N/A)

/* Makes an expression "not known", also setting its error message */
#define makeUnknown(expr_, ...) do { \
struct Expression *_expr = expr_; \
Expand Down Expand Up @@ -95,6 +98,25 @@ void rpn_Free(struct Expression *expr)
rpn_Init(expr);
}

void rpn_EnterShortCircuitOp(bool startShortCircuit)
{
nestedShortCircuitOps++;
if (startShortCircuit && shortCircuitAtDepth == 0)
shortCircuitAtDepth = nestedShortCircuitOps;
}

void rpn_LeaveShortCircuitOp(void)
{
nestedShortCircuitOps--;
if (shortCircuitAtDepth > nestedShortCircuitOps)
shortCircuitAtDepth = 0;
}

bool rpn_IsShortCircuited(void)
{
return shortCircuitAtDepth > 0 && shortCircuitAtDepth >= nestedShortCircuitOps;
}

/*
* Add symbols, constants and operators to expression
*/
Expand All @@ -106,6 +128,11 @@ void rpn_Number(struct Expression *expr, uint32_t i)

void rpn_Symbol(struct Expression *expr, char const *tzSym)
{
if (rpn_IsShortCircuited()) {
rpn_Number(expr, 0);
return;
}

struct Symbol *sym = sym_FindScopedSymbol(tzSym);

if (sym_IsPC(sym) && !sect_GetSymbolSection()) {
Expand Down Expand Up @@ -157,6 +184,11 @@ void rpn_BankSelf(struct Expression *expr)

void rpn_BankSymbol(struct Expression *expr, char const *tzSym)
{
if (rpn_IsShortCircuited()) {
rpn_Number(expr, 0);
return;
}

struct Symbol const *sym = sym_FindScopedSymbol(tzSym);

/* The @ symbol is treated differently. */
Expand Down Expand Up @@ -189,6 +221,11 @@ void rpn_BankSymbol(struct Expression *expr, char const *tzSym)

void rpn_BankSection(struct Expression *expr, char const *tzSectionName)
{
if (rpn_IsShortCircuited()) {
rpn_Number(expr, 0);
return;
}

rpn_Init(expr);

struct Section *pSection = out_FindSectionByName(tzSectionName);
Expand Down Expand Up @@ -321,6 +358,11 @@ static bool isDiffConstant(struct Expression const *src1,
void rpn_BinaryOp(enum RPNCommand op, struct Expression *expr,
const struct Expression *src1, const struct Expression *src2)
{
if (rpn_IsShortCircuited()) {
rpn_Number(expr, 0);
return;
}

expr->isSymbol = false;

/* First, check if the expression is known */
Expand Down
9 changes: 9 additions & 0 deletions test/asm/short-circuiting.asm
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@

printv 1 == 1 || undef_n == 42
printt "\n"

printv 1 == 2 && 0 < STRLEN("{undef_s}")
printt "\n"

printv (((1 == 1 || undef_a) || undef_b) || undef_c)
printt "\n"
Empty file added test/asm/short-circuiting.err
Empty file.
3 changes: 3 additions & 0 deletions test/asm/short-circuiting.out
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
$1
$0
$1

0 comments on commit 0fdd7ff

Please sign in to comment.