Add _Atomic and atomic ++, -- and op= operators

This commit is contained in:
Rui Ueyama 2020-09-15 11:30:56 +09:00
parent 80ea9d427c
commit d69a11dd25
4 changed files with 111 additions and 11 deletions

View File

@ -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
View File

@ -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),

View File

@ -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; }));

View File

@ -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++)