Skip to content

Commit

Permalink
parsec supports more compound assign operators
Browse files Browse the repository at this point in the history
  • Loading branch information
thejoshwolfe committed Nov 14, 2017
1 parent c1fde0e commit 4c2cdf6
Show file tree
Hide file tree
Showing 2 changed files with 213 additions and 117 deletions.
273 changes: 156 additions & 117 deletions src/parsec.cpp
Expand Up @@ -1034,7 +1034,7 @@ static AstNode *trans_binary_operator(Context *c, bool result_used, AstNode *blo
// unsigned/float division uses the operator
return trans_create_bin_op(c, block, stmt->getLHS(), BinOpTypeMod, stmt->getRHS());
} else {
// signed integer division uses @divTrunc
// signed integer division uses @rem
AstNode *fn_call = trans_create_node_builtin_fn_call_str(c, "rem");
AstNode *lhs = trans_expr(c, true, block, stmt->getLHS(), TransLValue);
if (lhs == nullptr) return nullptr;
Expand Down Expand Up @@ -1081,36 +1081,6 @@ static AstNode *trans_binary_operator(Context *c, bool result_used, AstNode *blo
return trans_create_bin_op(c, block, stmt->getLHS(), BinOpTypeBoolOr, stmt->getRHS());
case BO_Assign:
return trans_create_assign(c, result_used, block, stmt->getLHS(), stmt->getRHS());
case BO_MulAssign:
emit_warning(c, stmt->getLocStart(), "TODO handle more C binary operators: BO_MulAssign");
return nullptr;
case BO_DivAssign:
emit_warning(c, stmt->getLocStart(), "TODO handle more C binary operators: BO_DivAssign");
return nullptr;
case BO_RemAssign:
emit_warning(c, stmt->getLocStart(), "TODO handle more C binary operators: BO_RemAssign");
return nullptr;
case BO_AddAssign:
emit_warning(c, stmt->getLocStart(), "TODO handle more C binary operators: BO_AddAssign");
return nullptr;
case BO_SubAssign:
emit_warning(c, stmt->getLocStart(), "TODO handle more C binary operators: BO_SubAssign");
return nullptr;
case BO_ShlAssign:
emit_warning(c, stmt->getLocStart(), "TODO handle more C binary operators: BO_ShlAssign");
return nullptr;
case BO_ShrAssign:
emit_warning(c, stmt->getLocStart(), "TODO handle more C binary operators: BO_ShrAssign");
return nullptr;
case BO_AndAssign:
emit_warning(c, stmt->getLocStart(), "TODO handle more C binary operators: BO_AndAssign");
return nullptr;
case BO_XorAssign:
emit_warning(c, stmt->getLocStart(), "TODO handle more C binary operators: BO_XorAssign");
return nullptr;
case BO_OrAssign:
emit_warning(c, stmt->getLocStart(), "TODO handle more C binary operators: BO_OrAssign");
return nullptr;
case BO_Comma:
{
block = trans_create_node(c, NodeTypeBlock);
Expand All @@ -1123,112 +1093,181 @@ static AstNode *trans_binary_operator(Context *c, bool result_used, AstNode *blo
block->data.block.last_statement_is_result_expression = true;
return block;
}
}

zig_unreachable();
}

static AstNode *trans_compound_assign_operator(Context *c, bool result_used, AstNode *block, CompoundAssignOperator *stmt) {
switch (stmt->getOpcode()) {
case BO_MulAssign:
emit_warning(c, stmt->getLocStart(), "TODO handle more C compound assign operators: BO_MulAssign");
return nullptr;
case BO_DivAssign:
emit_warning(c, stmt->getLocStart(), "TODO handle more C compound assign operators: BO_DivAssign");
return nullptr;
case BO_RemAssign:
emit_warning(c, stmt->getLocStart(), "TODO handle more C compound assign operators: BO_RemAssign");
return nullptr;
case BO_AddAssign:
emit_warning(c, stmt->getLocStart(), "TODO handle more C compound assign operators: BO_AddAssign");
return nullptr;
case BO_SubAssign:
emit_warning(c, stmt->getLocStart(), "TODO handle more C compound assign operators: BO_SubAssign");
return nullptr;
case BO_ShlAssign:
emit_warning(c, stmt->getLocStart(), "TODO handle more C compound assign operators: BO_ShlAssign");
return nullptr;
case BO_ShrAssign: {
BinOpType bin_op = BinOpTypeBitShiftRight;
case BO_ShrAssign:
case BO_AndAssign:
case BO_XorAssign:
case BO_OrAssign:
zig_unreachable();
}

const SourceLocation &rhs_location = stmt->getRHS()->getLocStart();
AstNode *rhs_type = qual_type_to_log2_int_ref(c, stmt->getComputationLHSType(), rhs_location);
zig_unreachable();
}

bool use_intermediate_casts = stmt->getComputationLHSType().getTypePtr() != stmt->getComputationResultType().getTypePtr();
if (!use_intermediate_casts && !result_used) {
// simple common case, where the C and Zig are identical:
// lhs >>= rh* s
AstNode *lhs = trans_expr(c, true, block, stmt->getLHS(), TransLValue);
if (lhs == nullptr) return nullptr;
static AstNode *trans_create_compound_assign_shift(Context *c, bool result_used, AstNode *block, CompoundAssignOperator *stmt, BinOpType assign_op, BinOpType bin_op) {
const SourceLocation &rhs_location = stmt->getRHS()->getLocStart();
AstNode *rhs_type = qual_type_to_log2_int_ref(c, stmt->getComputationLHSType(), rhs_location);

AstNode *rhs = trans_expr(c, true, block, stmt->getRHS(), TransRValue);
if (rhs == nullptr) return nullptr;
AstNode *coerced_rhs = trans_create_node_fn_call_1(c, rhs_type, rhs);
bool use_intermediate_casts = stmt->getComputationLHSType().getTypePtr() != stmt->getComputationResultType().getTypePtr();
if (!use_intermediate_casts && !result_used) {
// simple common case, where the C and Zig are identical:
// lhs >>= rhs
AstNode *lhs = trans_expr(c, true, block, stmt->getLHS(), TransLValue);
if (lhs == nullptr) return nullptr;

return trans_create_node_bin_op(c, lhs, BinOpTypeAssignBitShiftRight, coerced_rhs);
} else {
// need more complexity. worst case, this looks like this:
// c: lhs >>= rhs
// zig: {
// zig: const _ref = &lhs;
// zig: *_ref = result_type(operation_type(*_ref) >> u5(rhs));
// zig: *_ref
// zig: }
// where u5 is the appropriate type
AstNode *rhs = trans_expr(c, true, block, stmt->getRHS(), TransRValue);
if (rhs == nullptr) return nullptr;
AstNode *coerced_rhs = trans_create_node_fn_call_1(c, rhs_type, rhs);

// TODO: avoid mess when we don't need the assignment value for chained assignments or anything.
AstNode *child_block = trans_create_node(c, NodeTypeBlock);
return trans_create_node_bin_op(c, lhs, assign_op, coerced_rhs);
} else {
// need more complexity. worst case, this looks like this:
// c: lhs >>= rhs
// zig: {
// zig: const _ref = &lhs;
// zig: *_ref = result_type(operation_type(*_ref) >> u5(rhs));
// zig: *_ref
// zig: }
// where u5 is the appropriate type

// const _ref = &lhs;
AstNode *lhs = trans_expr(c, true, child_block, stmt->getLHS(), TransLValue);
if (lhs == nullptr) return nullptr;
AstNode *addr_of_lhs = trans_create_node_addr_of(c, false, false, lhs);
// TODO: avoid name collisions with generated variable names
Buf* tmp_var_name = buf_create_from_str("_ref");
AstNode *tmp_var_decl = trans_create_node_var_decl_local(c, true, tmp_var_name, nullptr, addr_of_lhs);
child_block->data.block.statements.append(tmp_var_decl);
AstNode *child_block = trans_create_node(c, NodeTypeBlock);

// *_ref = result_type(operation_type(*_ref) >> u5(rhs));
// const _ref = &lhs;
AstNode *lhs = trans_expr(c, true, child_block, stmt->getLHS(), TransLValue);
if (lhs == nullptr) return nullptr;
AstNode *addr_of_lhs = trans_create_node_addr_of(c, false, false, lhs);
// TODO: avoid name collisions with generated variable names
Buf* tmp_var_name = buf_create_from_str("_ref");
AstNode *tmp_var_decl = trans_create_node_var_decl_local(c, true, tmp_var_name, nullptr, addr_of_lhs);
child_block->data.block.statements.append(tmp_var_decl);

AstNode *rhs = trans_expr(c, true, child_block, stmt->getRHS(), TransRValue);
if (rhs == nullptr) return nullptr;
AstNode *coerced_rhs = trans_create_node_fn_call_1(c, rhs_type, rhs);
// *_ref = result_type(operation_type(*_ref) >> u5(rhs));

AstNode *assign_statement = trans_create_node_bin_op(c,
trans_create_node_prefix_op(c, PrefixOpDereference,
trans_create_node_symbol(c, tmp_var_name)),
BinOpTypeAssign,
AstNode *rhs = trans_expr(c, true, child_block, stmt->getRHS(), TransRValue);
if (rhs == nullptr) return nullptr;
AstNode *coerced_rhs = trans_create_node_fn_call_1(c, rhs_type, rhs);

AstNode *assign_statement = trans_create_node_bin_op(c,
trans_create_node_prefix_op(c, PrefixOpDereference,
trans_create_node_symbol(c, tmp_var_name)),
BinOpTypeAssign,
trans_c_cast(c, rhs_location,
stmt->getComputationResultType(),
trans_create_node_bin_op(c,
trans_c_cast(c, rhs_location,
stmt->getComputationResultType(),
trans_create_node_bin_op(c,
trans_c_cast(c, rhs_location,
stmt->getComputationLHSType(),
trans_create_node_prefix_op(c, PrefixOpDereference,
trans_create_node_symbol(c, tmp_var_name))),
bin_op,
coerced_rhs)));
child_block->data.block.statements.append(assign_statement);

if (result_used) {
// *_ref
child_block->data.block.statements.append(
stmt->getComputationLHSType(),
trans_create_node_prefix_op(c, PrefixOpDereference,
trans_create_node_symbol(c, tmp_var_name)));
child_block->data.block.last_statement_is_result_expression = true;
}

return child_block;
}
trans_create_node_symbol(c, tmp_var_name))),
bin_op,
coerced_rhs)));
child_block->data.block.statements.append(assign_statement);

if (result_used) {
// *_ref
child_block->data.block.statements.append(
trans_create_node_prefix_op(c, PrefixOpDereference,
trans_create_node_symbol(c, tmp_var_name)));
child_block->data.block.last_statement_is_result_expression = true;
}
case BO_AndAssign:
emit_warning(c, stmt->getLocStart(), "TODO handle more C compound assign operators: BO_AndAssign");

return child_block;
}
}

static AstNode *trans_create_compound_assign(Context *c, bool result_used, AstNode *block, CompoundAssignOperator *stmt, BinOpType assign_op, BinOpType bin_op) {
if (!result_used) {
// simple common case, where the C and Zig are identical:
// lhs += rhs
AstNode *lhs = trans_expr(c, true, block, stmt->getLHS(), TransLValue);
if (lhs == nullptr) return nullptr;
AstNode *rhs = trans_expr(c, true, block, stmt->getRHS(), TransRValue);
if (rhs == nullptr) return nullptr;
return trans_create_node_bin_op(c, lhs, assign_op, rhs);
} else {
// need more complexity. worst case, this looks like this:
// c: lhs += rhs
// zig: {
// zig: const _ref = &lhs;
// zig: *_ref = *_ref + rhs;
// zig: *_ref
// zig: }

AstNode *child_block = trans_create_node(c, NodeTypeBlock);

// const _ref = &lhs;
AstNode *lhs = trans_expr(c, true, child_block, stmt->getLHS(), TransLValue);
if (lhs == nullptr) return nullptr;
AstNode *addr_of_lhs = trans_create_node_addr_of(c, false, false, lhs);
// TODO: avoid name collisions with generated variable names
Buf* tmp_var_name = buf_create_from_str("_ref");
AstNode *tmp_var_decl = trans_create_node_var_decl_local(c, true, tmp_var_name, nullptr, addr_of_lhs);
child_block->data.block.statements.append(tmp_var_decl);

// *_ref = *_ref + rhs;

AstNode *rhs = trans_expr(c, true, child_block, stmt->getRHS(), TransRValue);
if (rhs == nullptr) return nullptr;

AstNode *assign_statement = trans_create_node_bin_op(c,
trans_create_node_prefix_op(c, PrefixOpDereference,
trans_create_node_symbol(c, tmp_var_name)),
BinOpTypeAssign,
trans_create_node_bin_op(c,
trans_create_node_prefix_op(c, PrefixOpDereference,
trans_create_node_symbol(c, tmp_var_name)),
bin_op,
rhs));
child_block->data.block.statements.append(assign_statement);

// *_ref
child_block->data.block.statements.append(
trans_create_node_prefix_op(c, PrefixOpDereference,
trans_create_node_symbol(c, tmp_var_name)));
child_block->data.block.last_statement_is_result_expression = true;

return child_block;
}
}


static AstNode *trans_compound_assign_operator(Context *c, bool result_used, AstNode *block, CompoundAssignOperator *stmt) {
switch (stmt->getOpcode()) {
case BO_MulAssign:
if (qual_type_has_wrapping_overflow(c, stmt->getType()))
return trans_create_compound_assign(c, result_used, block, stmt, BinOpTypeAssignTimesWrap, BinOpTypeMultWrap);
else
return trans_create_compound_assign(c, result_used, block, stmt, BinOpTypeAssignTimes, BinOpTypeMult);
case BO_DivAssign:
emit_warning(c, stmt->getLocStart(), "TODO handle more C compound assign operators: BO_DivAssign");
return nullptr;
case BO_XorAssign:
emit_warning(c, stmt->getLocStart(), "TODO handle more C compound assign operators: BO_XorAssign");
case BO_RemAssign:
emit_warning(c, stmt->getLocStart(), "TODO handle more C compound assign operators: BO_RemAssign");
return nullptr;
case BO_AddAssign:
if (qual_type_has_wrapping_overflow(c, stmt->getType()))
return trans_create_compound_assign(c, result_used, block, stmt, BinOpTypeAssignPlusWrap, BinOpTypeAddWrap);
else
return trans_create_compound_assign(c, result_used, block, stmt, BinOpTypeAssignPlus, BinOpTypeAdd);
case BO_SubAssign:
if (qual_type_has_wrapping_overflow(c, stmt->getType()))
return trans_create_compound_assign(c, result_used, block, stmt, BinOpTypeAssignMinusWrap, BinOpTypeSubWrap);
else
return trans_create_compound_assign(c, result_used, block, stmt, BinOpTypeAssignMinus, BinOpTypeSub);
case BO_ShlAssign:
return trans_create_compound_assign_shift(c, result_used, block, stmt, BinOpTypeAssignBitShiftLeft, BinOpTypeBitShiftLeft);
case BO_ShrAssign:
return trans_create_compound_assign_shift(c, result_used, block, stmt, BinOpTypeAssignBitShiftRight, BinOpTypeBitShiftRight);
case BO_AndAssign:
return trans_create_compound_assign(c, result_used, block, stmt, BinOpTypeAssignBitAnd, BinOpTypeBinAnd);
case BO_XorAssign:
return trans_create_compound_assign(c, result_used, block, stmt, BinOpTypeAssignBitXor, BinOpTypeBinXor);
case BO_OrAssign:
emit_warning(c, stmt->getLocStart(), "TODO handle more C compound assign operators: BO_OrAssign");
return nullptr;
return trans_create_compound_assign(c, result_used, block, stmt, BinOpTypeAssignBitOr, BinOpTypeBinOr);
case BO_PtrMemD:
case BO_PtrMemI:
case BO_Assign:
Expand All @@ -1251,7 +1290,7 @@ static AstNode *trans_compound_assign_operator(Context *c, bool result_used, Ast
case BO_LAnd:
case BO_LOr:
case BO_Comma:
zig_panic("compound assign expected to be handled by binary operator");
zig_unreachable();
}

zig_unreachable();
Expand Down
57 changes: 57 additions & 0 deletions test/parsec.zig
Expand Up @@ -630,6 +630,63 @@ pub fn addCases(cases: &tests.ParseCContext) {
\\}
);

cases.addC("compound assignment operators",
\\void foo(void) {
\\ int a = 0;
\\ a += (a += 1);
\\ a -= (a -= 1);
\\ a *= (a *= 1);
\\ a &= (a &= 1);
\\ a |= (a |= 1);
\\ a ^= (a ^= 1);
\\ a >>= (a >>= 1);
\\ a <<= (a <<= 1);
\\}
,
\\export fn foo() {
\\ var a: c_int = 0;
\\ a += {
\\ const _ref = &a;
\\ (*_ref) = ((*_ref) + 1);
\\ *_ref
\\ };
\\ a -= {
\\ const _ref = &a;
\\ (*_ref) = ((*_ref) - 1);
\\ *_ref
\\ };
\\ a *= {
\\ const _ref = &a;
\\ (*_ref) = ((*_ref) * 1);
\\ *_ref
\\ };
\\ a &= {
\\ const _ref = &a;
\\ (*_ref) = ((*_ref) & 1);
\\ *_ref
\\ };
\\ a |= {
\\ const _ref = &a;
\\ (*_ref) = ((*_ref) | 1);
\\ *_ref
\\ };
\\ a ^= {
\\ const _ref = &a;
\\ (*_ref) = ((*_ref) ^ 1);
\\ *_ref
\\ };
\\ a >>= @import("std").math.Log2Int(c_int)({
\\ const _ref = &a;
\\ (*_ref) = c_int(c_int(*_ref) >> @import("std").math.Log2Int(c_int)(1));
\\ *_ref
\\ });
\\ a <<= @import("std").math.Log2Int(c_int)({
\\ const _ref = &a;
\\ (*_ref) = c_int(c_int(*_ref) << @import("std").math.Log2Int(c_int)(1));
\\ *_ref
\\ });
\\}
);
cases.addC("duplicate typedef",
\\typedef long foo;
\\typedef int bar;
Expand Down

0 comments on commit 4c2cdf6

Please sign in to comment.