Skip to content

Commit

Permalink
Fix atomic_fetch_op return value
Browse files Browse the repository at this point in the history
as reported by rui314/chibicc#101
  • Loading branch information
fuhsnn committed Mar 18, 2024
1 parent 4ff76a9 commit 62a00f3
Show file tree
Hide file tree
Showing 2 changed files with 110 additions and 72 deletions.
20 changes: 10 additions & 10 deletions include/stdatomic.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,17 +34,17 @@ typedef enum {
#define atomic_load_explicit(addr, order) (*(addr))
#define atomic_store_explicit(addr, val, order) (*(addr) = (val))

#define atomic_fetch_add(obj, val) (*(obj) += (val))
#define atomic_fetch_sub(obj, val) (*(obj) -= (val))
#define atomic_fetch_or(obj, val) (*(obj) |= (val))
#define atomic_fetch_xor(obj, val) (*(obj) ^= (val))
#define atomic_fetch_and(obj, val) (*(obj) &= (val))
#define atomic_fetch_add(addr, val) __builtin_atomic_fetch_add(addr, val)
#define atomic_fetch_sub(addr, val) __builtin_atomic_fetch_sub(addr, val)
#define atomic_fetch_or(addr, val) __builtin_atomic_fetch_or(addr, val)
#define atomic_fetch_xor(addr, val) __builtin_atomic_fetch_xor(addr, val)
#define atomic_fetch_and(addr, val) __builtin_atomic_fetch_and(addr, val)

#define atomic_fetch_add_explicit(obj, val, order) (*(obj) += (val))
#define atomic_fetch_sub_explicit(obj, val, order) (*(obj) -= (val))
#define atomic_fetch_or_explicit(obj, val, order) (*(obj) |= (val))
#define atomic_fetch_xor_explicit(obj, val, order) (*(obj) ^= (val))
#define atomic_fetch_and_explicit(obj, val, order) (*(obj) &= (val))
#define atomic_fetch_add_explicit(addr, val, order) __builtin_atomic_fetch_add(addr, val)
#define atomic_fetch_sub_explicit(addr, val, order) __builtin_atomic_fetch_sub(addr, val)
#define atomic_fetch_or_explicit(addr, val, order) __builtin_atomic_fetch_or(addr, val)
#define atomic_fetch_xor_explicit(addr, val, order) __builtin_atomic_fetch_xor(addr, val)
#define atomic_fetch_and_explicit(addr, val, order) __builtin_atomic_fetch_and(addr, val)

#define atomic_compare_exchange_weak(p, old, new) \
__builtin_compare_and_swap((p), (old), (new))
Expand Down
162 changes: 100 additions & 62 deletions parse.c
Original file line number Diff line number Diff line change
Expand Up @@ -2447,6 +2447,72 @@ static long double eval_double(Node *node) {
return eval_error(node->tok, "not a compile-time constant");
}

static Node *atomic_op(Node *binary, bool return_old) {
// ({
// T *addr = &obj; T old = *addr; T new;
// do {
// new = old op val;
// } while (!atomic_compare_exchange_strong(addr, &old, new));
//
// return_old ? old : new;
// })
Token *tok = binary->tok;
Node head = {0};
Node *cur = &head;

Obj *addr = new_lvar(NULL, pointer_to(binary->lhs->ty));
Obj *val = new_lvar(NULL, binary->rhs->ty);
Obj *old = new_lvar(NULL, binary->lhs->ty);
Obj *new = new_lvar(NULL, binary->lhs->ty);

cur = cur->next =
new_unary(ND_EXPR_STMT,
new_binary(ND_ASSIGN, new_var_node(addr, tok),
new_unary(ND_ADDR, binary->lhs, tok), tok),
tok);

cur = cur->next =
new_unary(ND_EXPR_STMT,
new_binary(ND_ASSIGN, new_var_node(val, tok), binary->rhs, tok),
tok);

cur = cur->next =
new_unary(ND_EXPR_STMT,
new_binary(ND_ASSIGN, new_var_node(old, tok),
new_unary(ND_DEREF, new_var_node(addr, tok), tok), tok),
tok);

Node *loop = new_node(ND_DO, tok);
loop->brk_label = new_unique_name();
loop->cont_label = new_unique_name();

Node *body = new_binary(ND_ASSIGN,
new_var_node(new, tok),
new_binary(binary->kind, new_var_node(old, tok),
new_var_node(val, tok), tok),
tok);

loop->then = new_node(ND_BLOCK, tok);
loop->then->body = new_unary(ND_EXPR_STMT, body, tok);

Node *cas = new_node(ND_CAS, tok);
cas->cas_addr = new_var_node(addr, tok);
cas->cas_old = new_unary(ND_ADDR, new_var_node(old, tok), tok);
cas->cas_new = new_var_node(new, tok);
loop->cond = new_unary(ND_NOT, cas, tok);

cur = cur->next = loop;

if (return_old)
cur->next = new_unary(ND_EXPR_STMT, new_var_node(old, tok), tok);
else
cur->next = new_unary(ND_EXPR_STMT, new_var_node(new, tok), tok);

Node *node = new_node(ND_STMT_EXPR, tok);
node->body = head.next;
return node;
}

// Convert op= operators to expressions containing an assignment.
//
// In general, `A op= C` is converted to ``tmp = &A, *tmp = *tmp op B`.
Expand All @@ -2458,6 +2524,10 @@ static Node *to_assign(Node *binary) {
add_type(binary->rhs);
Token *tok = binary->tok;

// If A is an atomic type, Convert `A op= B` to atomic_op_fetch(&A, B)
if (binary->lhs->ty->is_atomic)
return atomic_op(binary, false);

// Convert `A.x op= C` to `tmp = &A, (*tmp).x = (*tmp).x op C`.
if (is_bitfield(binary->lhs)) {
Obj *var = new_lvar(NULL, pointer_to(binary->lhs->lhs->ty));
Expand All @@ -2482,68 +2552,6 @@ static Node *to_assign(Node *binary) {
return new_binary(ND_CHAIN, expr1, expr4, tok);
}

// If A is an atomic type, Convert `A op= B` to
//
// ({
// T1 *addr = &A; T2 val = (B); T1 old = *addr; T1 new;
// do {
// new = old op val;
// } while (!atomic_compare_exchange_strong(addr, &old, new));
// new;
// })
if (binary->lhs->ty->is_atomic) {
Node head = {0};
Node *cur = &head;

Obj *addr = new_lvar(NULL, pointer_to(binary->lhs->ty));
Obj *val = new_lvar(NULL, binary->rhs->ty);
Obj *old = new_lvar(NULL, binary->lhs->ty);
Obj *new = new_lvar(NULL, binary->lhs->ty);

cur = cur->next =
new_unary(ND_EXPR_STMT,
new_binary(ND_ASSIGN, new_var_node(addr, tok),
new_unary(ND_ADDR, binary->lhs, tok), tok),
tok);

cur = cur->next =
new_unary(ND_EXPR_STMT,
new_binary(ND_ASSIGN, new_var_node(val, tok), binary->rhs, tok),
tok);

cur = cur->next =
new_unary(ND_EXPR_STMT,
new_binary(ND_ASSIGN, new_var_node(old, tok),
new_unary(ND_DEREF, new_var_node(addr, tok), tok), tok),
tok);

Node *loop = new_node(ND_DO, tok);
loop->brk_label = new_unique_name();
loop->cont_label = new_unique_name();

Node *body = new_binary(ND_ASSIGN,
new_var_node(new, tok),
new_binary(binary->kind, new_var_node(old, tok),
new_var_node(val, tok), tok),
tok);

loop->then = new_node(ND_BLOCK, tok);
loop->then->body = new_unary(ND_EXPR_STMT, body, tok);

Node *cas = new_node(ND_CAS, tok);
cas->cas_addr = new_var_node(addr, tok);
cas->cas_old = new_unary(ND_ADDR, new_var_node(old, tok), tok);
cas->cas_new = new_var_node(new, tok);
loop->cond = new_unary(ND_NOT, cas, tok);

cur = cur->next = loop;
cur = cur->next = new_unary(ND_EXPR_STMT, new_var_node(new, tok), tok);

Node *node = new_node(ND_STMT_EXPR, tok);
node->body = head.next;
return node;
}

// Convert `A op= B` to ``tmp = &A, *tmp = *tmp op B`.
Obj *var = new_lvar(NULL, pointer_to(binary->lhs->ty));

Expand Down Expand Up @@ -3563,6 +3571,36 @@ static Node *primary(Token **rest, Token *tok) {
return node;
}

if (!strncmp(tok->loc, "__builtin_atomic_fetch_", 23)) {
Token *start = tok;
tok = skip(tok->next, "(");
Node *obj = new_unary(ND_DEREF, assign(&tok, tok), start);
tok = skip(tok, ",");
Node *val = assign(&tok, tok);
*rest = skip(tok, ")");

Node *binary;
char *loc = start->loc + 23;
int len = start->len - 23;

if (!strncmp("add", loc, len))
binary = new_add(obj, val, start);
else if (!strncmp("sub", loc, len))
binary = new_sub(obj, val, start);
else if (!strncmp("and", loc, len))
binary = new_binary(ND_BITAND, obj, val, start);
else if (!strncmp("or", loc, len))
binary = new_binary(ND_BITOR, obj, val, start);
else if (!strncmp("xor", loc, len))
binary = new_binary(ND_BITXOR, obj, val, start);
else
error_tok(start, "unsupported atomic fetch op");

add_type(binary->lhs);
add_type(binary->rhs);
return atomic_op(binary, true);
}

if (equal(tok, "__builtin_offsetof")) {
tok = skip(tok->next, "(");
Type *ty = typename(&tok, tok);
Expand Down

0 comments on commit 62a00f3

Please sign in to comment.