translate-c: support pre increment and decrement operators

This commit is contained in:
Andrew Kelley 2017-11-24 16:36:39 -05:00
parent 741504862c
commit 68312afcdf
2 changed files with 133 additions and 39 deletions

View File

@ -1524,47 +1524,93 @@ static AstNode *trans_create_post_crement(Context *c, bool result_used, AstNode
trans_expr(c, true, block, op_expr, TransLValue),
assign_op,
trans_create_node_unsigned(c, 1));
} else {
// worst case
// c: expr++
// zig: {
// zig: const _ref = &expr;
// zig: const _tmp = *_ref;
// zig: *_ref += 1;
// zig: _tmp
// zig: }
AstNode *child_block = trans_create_node(c, NodeTypeBlock);
}
// worst case
// c: expr++
// zig: {
// zig: const _ref = &expr;
// zig: const _tmp = *_ref;
// zig: *_ref += 1;
// zig: _tmp
// zig: }
AstNode *child_block = trans_create_node(c, NodeTypeBlock);
// const _ref = &expr;
AstNode *expr = trans_expr(c, true, child_block, op_expr, TransLValue);
if (expr == nullptr) return nullptr;
AstNode *addr_of_expr = trans_create_node_addr_of(c, false, false, expr);
// TODO: avoid name collisions with generated variable names
Buf* ref_var_name = buf_create_from_str("_ref");
AstNode *ref_var_decl = trans_create_node_var_decl_local(c, true, ref_var_name, nullptr, addr_of_expr);
child_block->data.block.statements.append(ref_var_decl);
// const _ref = &expr;
AstNode *expr = trans_expr(c, true, child_block, op_expr, TransLValue);
if (expr == nullptr) return nullptr;
AstNode *addr_of_expr = trans_create_node_addr_of(c, false, false, expr);
// TODO: avoid name collisions with generated variable names
Buf* ref_var_name = buf_create_from_str("_ref");
AstNode *ref_var_decl = trans_create_node_var_decl_local(c, true, ref_var_name, nullptr, addr_of_expr);
child_block->data.block.statements.append(ref_var_decl);
// const _tmp = *_ref;
Buf* tmp_var_name = buf_create_from_str("_tmp");
AstNode *tmp_var_decl = trans_create_node_var_decl_local(c, true, tmp_var_name, nullptr,
trans_create_node_prefix_op(c, PrefixOpDereference,
trans_create_node_symbol(c, ref_var_name)));
child_block->data.block.statements.append(tmp_var_decl);
// const _tmp = *_ref;
Buf* tmp_var_name = buf_create_from_str("_tmp");
AstNode *tmp_var_decl = trans_create_node_var_decl_local(c, true, tmp_var_name, nullptr,
trans_create_node_prefix_op(c, PrefixOpDereference,
trans_create_node_symbol(c, ref_var_name)));
child_block->data.block.statements.append(tmp_var_decl);
// *_ref += 1;
AstNode *assign_statement = trans_create_node_bin_op(c,
trans_create_node_prefix_op(c, PrefixOpDereference,
trans_create_node_symbol(c, ref_var_name)),
// *_ref += 1;
AstNode *assign_statement = trans_create_node_bin_op(c,
trans_create_node_prefix_op(c, PrefixOpDereference,
trans_create_node_symbol(c, ref_var_name)),
assign_op,
trans_create_node_unsigned(c, 1));
child_block->data.block.statements.append(assign_statement);
// _tmp
child_block->data.block.statements.append(trans_create_node_symbol(c, tmp_var_name));
child_block->data.block.last_statement_is_result_expression = true;
return child_block;
}
static AstNode *trans_create_pre_crement(Context *c, bool result_used, AstNode *block, UnaryOperator *stmt, BinOpType assign_op) {
Expr *op_expr = stmt->getSubExpr();
if (!result_used) {
// common case
// c: ++expr
// zig: expr += 1
return trans_create_node_bin_op(c,
trans_expr(c, true, block, op_expr, TransLValue),
assign_op,
trans_create_node_unsigned(c, 1));
child_block->data.block.statements.append(assign_statement);
// _tmp
child_block->data.block.statements.append(trans_create_node_symbol(c, tmp_var_name));
child_block->data.block.last_statement_is_result_expression = true;
return child_block;
}
// worst case
// c: ++expr
// zig: {
// zig: const _ref = &expr;
// zig: *_ref += 1;
// zig: *_ref
// zig: }
AstNode *child_block = trans_create_node(c, NodeTypeBlock);
// const _ref = &expr;
AstNode *expr = trans_expr(c, true, child_block, op_expr, TransLValue);
if (expr == nullptr) return nullptr;
AstNode *addr_of_expr = trans_create_node_addr_of(c, false, false, expr);
// TODO: avoid name collisions with generated variable names
Buf* ref_var_name = buf_create_from_str("_ref");
AstNode *ref_var_decl = trans_create_node_var_decl_local(c, true, ref_var_name, nullptr, addr_of_expr);
child_block->data.block.statements.append(ref_var_decl);
// *_ref += 1;
AstNode *assign_statement = trans_create_node_bin_op(c,
trans_create_node_prefix_op(c, PrefixOpDereference,
trans_create_node_symbol(c, ref_var_name)),
assign_op,
trans_create_node_unsigned(c, 1));
child_block->data.block.statements.append(assign_statement);
// *_ref
AstNode *deref_expr = trans_create_node_prefix_op(c, PrefixOpDereference,
trans_create_node_symbol(c, ref_var_name));
child_block->data.block.statements.append(deref_expr);
child_block->data.block.last_statement_is_result_expression = true;
return child_block;
}
static AstNode *trans_unary_operator(Context *c, bool result_used, AstNode *block, UnaryOperator *stmt) {
@ -1580,11 +1626,15 @@ static AstNode *trans_unary_operator(Context *c, bool result_used, AstNode *bloc
else
return trans_create_post_crement(c, result_used, block, stmt, BinOpTypeAssignMinus);
case UO_PreInc:
emit_warning(c, stmt->getLocStart(), "TODO handle C translation UO_PreInc");
return nullptr;
if (qual_type_has_wrapping_overflow(c, stmt->getType()))
return trans_create_pre_crement(c, result_used, block, stmt, BinOpTypeAssignPlusWrap);
else
return trans_create_pre_crement(c, result_used, block, stmt, BinOpTypeAssignPlus);
case UO_PreDec:
emit_warning(c, stmt->getLocStart(), "TODO handle C translation UO_PreDec");
return nullptr;
if (qual_type_has_wrapping_overflow(c, stmt->getType()))
return trans_create_pre_crement(c, result_used, block, stmt, BinOpTypeAssignMinusWrap);
else
return trans_create_pre_crement(c, result_used, block, stmt, BinOpTypeAssignMinus);
case UO_AddrOf:
{
AstNode *value_node = trans_expr(c, result_used, block, stmt->getSubExpr(), TransLValue);

View File

@ -805,6 +805,50 @@ pub fn addCases(cases: &tests.TranslateCContext) {
\\}
);
cases.addC("pre increment/decrement",
\\void foo(void) {
\\ int i = 0;
\\ unsigned u = 0;
\\ ++i;
\\ --i;
\\ ++u;
\\ --u;
\\ i = ++i;
\\ i = --i;
\\ u = ++u;
\\ u = --u;
\\}
,
\\export fn foo() {
\\ var i: c_int = 0;
\\ var u: c_uint = c_uint(0);
\\ i += 1;
\\ i -= 1;
\\ u +%= 1;
\\ u -%= 1;
\\ i = {
\\ const _ref = &i;
\\ (*_ref) += 1;
\\ *_ref
\\ };
\\ i = {
\\ const _ref = &i;
\\ (*_ref) -= 1;
\\ *_ref
\\ };
\\ u = {
\\ const _ref = &u;
\\ (*_ref) +%= 1;
\\ *_ref
\\ };
\\ u = {
\\ const _ref = &u;
\\ (*_ref) -%= 1;
\\ *_ref
\\ };
\\}
);
cases.addC("do loop",
\\void foo(void) {
\\ int a = 2;