Skip to content

Commit

Permalink
Add atomic_compare_exchange
Browse files Browse the repository at this point in the history
  • Loading branch information
rui314 committed Dec 7, 2020
1 parent fb49370 commit ca27455
Show file tree
Hide file tree
Showing 6 changed files with 122 additions and 0 deletions.
6 changes: 6 additions & 0 deletions chibicc.h
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,7 @@ typedef enum {
ND_CAST, // Type cast
ND_MEMZERO, // Zero-clear a stack variable
ND_ASM, // "asm"
ND_CAS, // Atomic compare-and-swap
} NodeKind;

// AST node type
Expand Down Expand Up @@ -271,6 +272,11 @@ struct Node {
// "asm" string literal
char *asm_str;

// Atomic compare-and-swap
Node *cas_addr;
Node *cas_old;
Node *cas_new;

// Variable
Obj *var;

Expand Down
40 changes: 40 additions & 0 deletions codegen.c
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,26 @@ int align_to(int n, int align) {
return (n + align - 1) / align * align;
}

static char *reg_dx(int sz) {
switch (sz) {
case 1: return "%dl";
case 2: return "%dx";
case 4: return "%edx";
case 8: return "%rdx";
}
unreachable();
}

static char *reg_ax(int sz) {
switch (sz) {
case 1: return "%al";
case 2: return "%ax";
case 4: return "%eax";
case 8: return "%rax";
}
unreachable();
}

// Compute the absolute address of a given node.
// It's an error if a given node does not reside in memory.
static void gen_addr(Node *node) {
Expand Down Expand Up @@ -943,6 +963,26 @@ static void gen_expr(Node *node) {
case ND_LABEL_VAL:
println(" lea %s(%%rip), %%rax", node->unique_label);
return;
case ND_CAS: {
gen_expr(node->cas_addr);
push();
gen_expr(node->cas_new);
push();
gen_expr(node->cas_old);
println(" mov %%rax, %%r8");
load(node->cas_old->ty->base);
pop("%rdx"); // new
pop("%rdi"); // addr

int sz = node->cas_addr->ty->base->size;
println(" lock cmpxchg %s, (%%rdi)", reg_dx(sz));
println(" sete %%cl");
println(" je 1f");
println(" mov %s, (%%r8)", reg_ax(sz));
println("1:");
println(" movzbl %%cl, %%eax");
return;
}
}

switch (node->lhs->ty->kind) {
Expand Down
10 changes: 10 additions & 0 deletions include/stdatomic.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#ifndef __STDATOMIC_H
#define __STDATOMIC_H

#define atomic_compare_exchange_weak(p, old, new) \
__builtin_compare_and_swap((p), (old), (new))

#define atomic_compare_exchange_strong(p, old, new) \
__builtin_compare_and_swap((p), (old), (new))

#endif
12 changes: 12 additions & 0 deletions parse.c
Original file line number Diff line number Diff line change
Expand Up @@ -2942,6 +2942,18 @@ static Node *primary(Token **rest, Token *tok) {
return new_num(2, start);
}

if (equal(tok, "__builtin_compare_and_swap")) {
Node *node = new_node(ND_CAS, tok);
tok = skip(tok->next, "(");
node->cas_addr = assign(&tok, tok);
tok = skip(tok, ",");
node->cas_old = assign(&tok, tok);
tok = skip(tok, ",");
node->cas_new = assign(&tok, tok);
*rest = skip(tok, ")");
return node;
}

if (tok->kind == TK_IDENT) {
// Variable or enum constant
VarScope *sc = find_var(tok);
Expand Down
43 changes: 43 additions & 0 deletions test/atomic.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
#include "test.h"
#include <stdatomic.h>
#include <pthread.h>

static int incr(int *p) {
int oldval = *p;
int newval;
do {
newval = oldval + 1;
} while (!atomic_compare_exchange_weak(p, &oldval, newval));
return newval;
}

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

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

pthread_t thr1;
pthread_t thr2;

pthread_create(&thr1, NULL, add, &x);
pthread_create(&thr2, NULL, add, &x);

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

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

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

printf("OK\n");
return 0;
}
11 changes: 11 additions & 0 deletions type.c
Original file line number Diff line number Diff line change
Expand Up @@ -287,5 +287,16 @@ void add_type(Node *node) {
case ND_LABEL_VAL:
node->ty = pointer_to(ty_void);
return;
case ND_CAS:
add_type(node->cas_addr);
add_type(node->cas_old);
add_type(node->cas_new);
node->ty = ty_bool;

if (node->cas_addr->ty->kind != TY_PTR)
error_tok(node->cas_addr->tok, "pointer expected");
if (node->cas_old->ty->kind != TY_PTR)
error_tok(node->cas_old->tok, "pointer expected");
return;
}
}

0 comments on commit ca27455

Please sign in to comment.