Add atomic_compare_exchange

This commit is contained in:
Rui Ueyama 2020-09-15 11:30:56 +09:00
parent fb4937024d
commit ca27455b92
6 changed files with 122 additions and 0 deletions

View File

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

View File

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

10
include/stdatomic.h Normal file
View File

@ -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
parse.c
View File

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

43
test/atomic.c Normal file
View File

@ -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
type.c
View File

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