mirror of https://github.com/rui314/chibicc
Add _Atomic and atomic ++, -- and op= operators
This commit is contained in:
parent
80ea9d427c
commit
d69a11dd25
|
@ -278,6 +278,10 @@ struct Node {
|
|||
Node *cas_old;
|
||||
Node *cas_new;
|
||||
|
||||
// Atomic op= operators
|
||||
Obj *atomic_addr;
|
||||
Node *atomic_expr;
|
||||
|
||||
// Variable
|
||||
Obj *var;
|
||||
|
||||
|
@ -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
|
||||
|
|
82
parse.c
82
parse.c
|
@ -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.
|
||||
|
@ -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");
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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++)
|
||||
|
@ -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),
|
||||
|
|
|
@ -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 {
|
||||
|
@ -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; }));
|
||||
|
|
|
@ -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++)
|
||||
|
|
Loading…
Reference in New Issue