Skip to content

Commit

Permalink
Add _Atomic and atomic ++, -- and op= operators
Browse files Browse the repository at this point in the history
  • Loading branch information
rui314 committed Dec 7, 2020
1 parent 80ea9d4 commit d69a11d
Show file tree
Hide file tree
Showing 4 changed files with 111 additions and 11 deletions.
5 changes: 5 additions & 0 deletions chibicc.h
Original file line number Diff line number Diff line change
Expand Up @@ -278,6 +278,10 @@ struct Node {
Node *cas_old;
Node *cas_new;

// Atomic op= operators
Obj *atomic_addr;
Node *atomic_expr;

// Variable
Obj *var;

Expand Down Expand Up @@ -318,6 +322,7 @@ struct Type {
int size; // sizeof() value
int align; // alignment
bool is_unsigned; // unsigned or signed
bool is_atomic; // true if _Atomic
Type *origin; // for type compatibility check

// Pointer-to or array-of type. We intentionally use the same member
Expand Down
82 changes: 80 additions & 2 deletions parse.c
Original file line number Diff line number Diff line change
Expand Up @@ -399,6 +399,7 @@ static Type *declspec(Token **rest, Token *tok, VarAttr *attr) {

Type *ty = ty_int;
int counter = 0;
bool is_atomic = false;

while (is_typename(tok)) {
// Handle storage class specifiers.
Expand Down Expand Up @@ -433,6 +434,16 @@ static Type *declspec(Token **rest, Token *tok, VarAttr *attr) {
consume(&tok, tok, "__restrict__") || consume(&tok, tok, "_Noreturn"))
continue;

if (equal(tok, "_Atomic")) {
tok = tok->next;
if (equal(tok , "(")) {
ty = typename(&tok, tok->next);
tok = skip(tok, ")");
}
is_atomic = true;
continue;
}

if (equal(tok, "_Alignas")) {
if (!attr)
error_tok(tok, "_Alignas is not allowed in this context");
Expand Down Expand Up @@ -559,6 +570,11 @@ static Type *declspec(Token **rest, Token *tok, VarAttr *attr) {
tok = tok->next;
}

if (is_atomic) {
ty = copy_type(ty);
ty->is_atomic = true;
}

*rest = tok;
return ty;
}
Expand Down Expand Up @@ -1487,7 +1503,7 @@ static bool is_typename(Token *tok) {
"typedef", "enum", "static", "extern", "_Alignas", "signed", "unsigned",
"const", "volatile", "auto", "register", "restrict", "__restrict",
"__restrict__", "_Noreturn", "float", "double", "typeof", "inline",
"_Thread_local", "__thread",
"_Thread_local", "__thread", "_Atomic",
};

for (int i = 0; i < sizeof(kw) / sizeof(*kw); i++)
Expand Down Expand Up @@ -2043,7 +2059,69 @@ static Node *to_assign(Node *binary) {
return new_binary(ND_COMMA, expr1, expr4, tok);
}

// Convert `A op= C` to ``tmp = &A, *tmp = *tmp op B`.
// 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 = {};
Node *cur = &head;

Obj *addr = new_lvar("", pointer_to(binary->lhs->ty));
Obj *val = new_lvar("", binary->rhs->ty);
Obj *old = new_lvar("", binary->lhs->ty);
Obj *new = new_lvar("", 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("", pointer_to(binary->lhs->ty));

Node *expr1 = new_binary(ND_ASSIGN, new_var_node(var, tok),
Expand Down
33 changes: 25 additions & 8 deletions test/atomic.c
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
#include <stdatomic.h>
#include <pthread.h>

static int incr(int *p) {
static int incr(_Atomic int *p) {
int oldval = *p;
int newval;
do {
Expand All @@ -11,32 +11,49 @@ static int incr(int *p) {
return newval;
}

static int add(void *arg) {
int *x = arg;
static int add1(void *arg) {
_Atomic int *x = arg;
for (int i = 0; i < 1000*1000; i++)
incr(x);
return 0;
}

static int add2(void *arg) {
_Atomic int *x = arg;
for (int i = 0; i < 1000*1000; i++)
(*x)++;
return 0;
}

static int add3(void *arg) {
_Atomic int *x = arg;
for (int i = 0; i < 1000*1000; i++)
*x += 5;
return 0;
}

static int add_millions(void) {
int x = 0;
_Atomic int x = 0;

pthread_t thr1;
pthread_t thr2;
pthread_t thr3;

pthread_create(&thr1, NULL, add, &x);
pthread_create(&thr2, NULL, add, &x);
pthread_create(&thr1, NULL, add1, &x);
pthread_create(&thr2, NULL, add2, &x);
pthread_create(&thr3, NULL, add3, &x);

for (int i = 0; i < 1000*1000; i++)
incr(&x);
x--;

pthread_join(thr1, NULL);
pthread_join(thr2, NULL);
pthread_join(thr3, NULL);
return x;
}

int main() {
ASSERT(3*1000*1000, add_millions());
ASSERT(6*1000*1000, add_millions());

ASSERT(3, ({ int x=3; atomic_exchange(&x, 5); }));
ASSERT(5, ({ int x=3; atomic_exchange(&x, 5); x; }));
Expand Down
2 changes: 1 addition & 1 deletion tokenize.c
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,7 @@ static bool is_keyword(Token *tok) {
"default", "extern", "_Alignof", "_Alignas", "do", "signed",
"unsigned", "const", "volatile", "auto", "register", "restrict",
"__restrict", "__restrict__", "_Noreturn", "float", "double",
"typeof", "asm", "_Thread_local", "__thread",
"typeof", "asm", "_Thread_local", "__thread", "_Atomic",
};

for (int i = 0; i < sizeof(kw) / sizeof(*kw); i++)
Expand Down

0 comments on commit d69a11d

Please sign in to comment.