Skip to content

Commit

Permalink
Add atomic_fetch builtins.
Browse files Browse the repository at this point in the history
  • Loading branch information
lerno committed Sep 15, 2023
1 parent d129cd4 commit f6e18de
Show file tree
Hide file tree
Showing 7 changed files with 193 additions and 1 deletion.
1 change: 1 addition & 0 deletions releasenotes.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
## 0.5.0 Change List

### Changes / improvements
- Added `$$atomic_fetch_*` builtins.
- vectors may now contain pointers.
- `void!` does not convert to `anyfault`.
- `$$masked_load` / `$$masked_store` / `$$gather` / `$$scatter` for vector masked load/store.
Expand Down
6 changes: 6 additions & 0 deletions src/compiler/compiler_internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -2935,6 +2935,12 @@ INLINE bool type_is_number_or_bool(Type *type)
return (kind >= TYPE_BOOL) && (kind <= TYPE_FLOAT_LAST);
}

INLINE bool type_is_number(Type *type)
{
DECL_TYPE_KIND_REAL(kind, type);
return (kind >= TYPE_I8) && (kind <= TYPE_FLOAT_LAST);
}

INLINE bool type_is_numeric(Type *type)
{
RETRY:;
Expand Down
10 changes: 10 additions & 0 deletions src/compiler/enums.h
Original file line number Diff line number Diff line change
Expand Up @@ -841,6 +841,16 @@ typedef enum
BUILTIN_ABS,
BUILTIN_ATOMIC_LOAD,
BUILTIN_ATOMIC_STORE,
BUILTIN_ATOMIC_FETCH_ADD,
BUILTIN_ATOMIC_FETCH_SUB,
BUILTIN_ATOMIC_FETCH_AND,
BUILTIN_ATOMIC_FETCH_NAND,
BUILTIN_ATOMIC_FETCH_OR,
BUILTIN_ATOMIC_FETCH_XOR,
BUILTIN_ATOMIC_FETCH_MAX,
BUILTIN_ATOMIC_FETCH_MIN,
BUILTIN_ATOMIC_FETCH_INC_WRAP,
BUILTIN_ATOMIC_FETCH_DEC_WRAP,
BUILTIN_BITREVERSE,
BUILTIN_BSWAP,
BUILTIN_CEIL,
Expand Down
70 changes: 70 additions & 0 deletions src/compiler/llvm_codegen_builtins.c
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,64 @@ INLINE void llvm_emit_atomic_store(GenContext *c, BEValue *result_value, Expr *e
}
}

INLINE void llvm_emit_atomic_fetch(GenContext *c, BuiltinFunction func, BEValue *result_value, Expr *expr)
{
BEValue value;
llvm_emit_expr(c, &value, expr->call_expr.arguments[0]);
llvm_emit_expr(c, result_value, expr->call_expr.arguments[1]);
llvm_value_rvalue(c, &value);
bool is_float = type_is_float(result_value->type);
bool is_unsigned = !is_float && type_is_unsigned(result_value->type);
LLVMAtomicRMWBinOp op;
static LLVMAtomicRMWBinOp LLVMAtomicRMWBinOpUIncWrap = LLVMAtomicRMWBinOpFMin + 1;
static LLVMAtomicRMWBinOp LLVMAtomicRMWBinOpUDecWrap = LLVMAtomicRMWBinOpFMin + 2;
switch (func)
{
case BUILTIN_ATOMIC_FETCH_ADD:
op = is_float ? LLVMAtomicRMWBinOpFAdd : LLVMAtomicRMWBinOpAdd;
break;
case BUILTIN_ATOMIC_FETCH_SUB:
op = is_float ? LLVMAtomicRMWBinOpFAdd : LLVMAtomicRMWBinOpAdd;
break;
case BUILTIN_ATOMIC_FETCH_MAX:
op = is_float ? LLVMAtomicRMWBinOpFMax : (is_unsigned ? LLVMAtomicRMWBinOpUMax : LLVMAtomicRMWBinOpMax);
break;
case BUILTIN_ATOMIC_FETCH_MIN:
op = is_float ? LLVMAtomicRMWBinOpFMin : (is_unsigned ? LLVMAtomicRMWBinOpUMin : LLVMAtomicRMWBinOpMin);
break;
case BUILTIN_ATOMIC_FETCH_OR:
op = LLVMAtomicRMWBinOpOr;
break;
case BUILTIN_ATOMIC_FETCH_XOR:
op = LLVMAtomicRMWBinOpXor;
break;
case BUILTIN_ATOMIC_FETCH_NAND:
op = LLVMAtomicRMWBinOpNand;
break;
case BUILTIN_ATOMIC_FETCH_AND:
op = LLVMAtomicRMWBinOpAnd;
break;
case BUILTIN_ATOMIC_FETCH_INC_WRAP:
op = LLVMAtomicRMWBinOpUIncWrap;
break;
case BUILTIN_ATOMIC_FETCH_DEC_WRAP:
op = LLVMAtomicRMWBinOpUDecWrap;
break;
default:
UNREACHABLE
}
LLVMValueRef res = LLVMBuildAtomicRMW(c->builder, op, value.value, llvm_load_value(c, result_value),
llvm_atomic_ordering(expr->call_expr.arguments[3]->const_expr.ixx.i.low),
false);
if (expr->call_expr.arguments[2]->const_expr.b) LLVMSetVolatile(res, true);
uint64_t alignment = expr->call_expr.arguments[3]->const_expr.ixx.i.low;
if (alignment)
{
LLVMSetAlignment(res, alignment);
}
llvm_value_set(result_value, res, result_value->type);
}

INLINE void llvm_emit_atomic_load(GenContext *c, BEValue *result_value, Expr *expr)
{
llvm_emit_expr(c, result_value, expr->call_expr.arguments[0]);
Expand Down Expand Up @@ -747,6 +805,18 @@ void llvm_emit_builtin_call(GenContext *c, BEValue *result_value, Expr *expr)
case BUILTIN_ATOMIC_STORE:
llvm_emit_atomic_store(c, result_value, expr);
return;
case BUILTIN_ATOMIC_FETCH_ADD:
case BUILTIN_ATOMIC_FETCH_INC_WRAP:
case BUILTIN_ATOMIC_FETCH_NAND:
case BUILTIN_ATOMIC_FETCH_AND:
case BUILTIN_ATOMIC_FETCH_OR:
case BUILTIN_ATOMIC_FETCH_XOR:
case BUILTIN_ATOMIC_FETCH_MAX:
case BUILTIN_ATOMIC_FETCH_MIN:
case BUILTIN_ATOMIC_FETCH_SUB:
case BUILTIN_ATOMIC_FETCH_DEC_WRAP:
llvm_emit_atomic_fetch(c, func, result_value, expr);
return;
case BUILTIN_ATOMIC_LOAD:
llvm_emit_atomic_load(c, result_value, expr);
return;
Expand Down
95 changes: 95 additions & 0 deletions src/compiler/sema_builtins.c
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ typedef enum
BA_VEC,
BA_NUMVEC,
BA_PTRVEC,
BA_NUM,
} BuiltinArg;

static bool sema_check_builtin_args_match(Expr **args, size_t arg_len);
Expand Down Expand Up @@ -107,6 +108,9 @@ static bool sema_check_builtin_args(Expr **args, BuiltinArg *arg_type, size_t ar
case BA_BOOL:
if (type == type_bool) continue;
RETURN_SEMA_ERROR(arg, "Expected a bool.");
case BA_NUM:
if (type_is_number(type)) continue;
RETURN_SEMA_ERROR(arg, "Expected an integer or a float.");
case BA_NUMLIKE:
if (type_flat_is_numlike(type)) continue;
RETURN_SEMA_ERROR(arg, "Expected a number or vector.");
Expand Down Expand Up @@ -719,6 +723,86 @@ bool sema_expr_analyse_builtin_call(SemaContext *context, Expr *expr)
rtype = args[1]->type;
break;
}
case BUILTIN_ATOMIC_FETCH_INC_WRAP:
case BUILTIN_ATOMIC_FETCH_DEC_WRAP:
{
assert(arg_count == 5);
if (!sema_check_builtin_args(args, (BuiltinArg[]) { BA_POINTER, BA_INTEGER }, 2)) return false;
Type *original = type_flatten(args[0]->type);
Type *val = type_flatten(args[1]->type);
if (!type_is_unsigned(val) )
{
RETURN_SEMA_ERROR(args[1], "Expected an unsigned integer.");
}
if (original != type_voidptr)
{
Type *pointer = type_flatten(original->pointer);
if (!type_is_unsigned(pointer))
{
RETURN_SEMA_ERROR(args[0], "Expected a pointer to an unsigned integer.");
}
if (!cast_implicit(context, args[1], original->pointer)) return false;
}
if (!expr_is_const(args[2])) RETURN_SEMA_ERROR(args[2], "'is_volatile' must be a compile time constant.");
if (!expr_is_const(args[3])) RETURN_SEMA_ERROR(args[3], "Ordering must be a compile time constant.");
if (!is_valid_atomicity(args[3])) return false;
switch (args[3]->const_expr.ixx.i.low)
{
case ATOMIC_UNORDERED:
RETURN_SEMA_ERROR(args[3], "'unordered' is not valid ordering.");
}
if (!sema_check_alignment_expression(context, args[4])) return false;
rtype = args[1]->type;
break;
}
case BUILTIN_ATOMIC_FETCH_AND:
case BUILTIN_ATOMIC_FETCH_NAND:
case BUILTIN_ATOMIC_FETCH_OR:
case BUILTIN_ATOMIC_FETCH_XOR:
{
assert(arg_count == 5);
if (!sema_check_builtin_args(args, (BuiltinArg[]) { BA_POINTER, BA_INTEGER }, 2)) return false;
Type *original = type_flatten(args[0]->type);
if (original != type_voidptr)
{
if (!cast_implicit(context, args[1], original->pointer)) return false;
}
if (!expr_is_const(args[2])) RETURN_SEMA_ERROR(args[2], "'is_volatile' must be a compile time constant.");
if (!expr_is_const(args[3])) RETURN_SEMA_ERROR(args[3], "Ordering must be a compile time constant.");
if (!is_valid_atomicity(args[3])) return false;
switch (args[3]->const_expr.ixx.i.low)
{
case ATOMIC_UNORDERED:
RETURN_SEMA_ERROR(args[3], "'unordered' is not valid ordering.");
}
if (!sema_check_alignment_expression(context, args[4])) return false;
rtype = args[1]->type;
break;
}
case BUILTIN_ATOMIC_FETCH_ADD:
case BUILTIN_ATOMIC_FETCH_SUB:
case BUILTIN_ATOMIC_FETCH_MAX:
case BUILTIN_ATOMIC_FETCH_MIN:
{
assert(arg_count == 5);
if (!sema_check_builtin_args(args, (BuiltinArg[]) { BA_POINTER, BA_NUM }, 2)) return false;
Type *original = type_flatten(args[0]->type);
if (original != type_voidptr)
{
if (!cast_implicit(context, args[1], original->pointer)) return false;
}
if (!expr_is_const(args[2])) RETURN_SEMA_ERROR(args[2], "'is_volatile' must be a compile time constant.");
if (!expr_is_const(args[3])) RETURN_SEMA_ERROR(args[3], "Ordering must be a compile time constant.");
if (!is_valid_atomicity(args[3])) return false;
switch (args[3]->const_expr.ixx.i.low)
{
case ATOMIC_UNORDERED:
RETURN_SEMA_ERROR(args[3], "'unordered' is not valid ordering.");
}
if (!sema_check_alignment_expression(context, args[4])) return false;
rtype = args[1]->type;
break;
}
case BUILTIN_ATOMIC_STORE:
{
assert(arg_count == 4);
Expand Down Expand Up @@ -855,6 +939,17 @@ static inline int builtin_expected_args(BuiltinFunction func)
case BUILTIN_GATHER:
case BUILTIN_SCATTER:
return 4;
case BUILTIN_ATOMIC_FETCH_ADD:
case BUILTIN_ATOMIC_FETCH_INC_WRAP:
case BUILTIN_ATOMIC_FETCH_NAND:
case BUILTIN_ATOMIC_FETCH_AND:
case BUILTIN_ATOMIC_FETCH_OR:
case BUILTIN_ATOMIC_FETCH_XOR:
case BUILTIN_ATOMIC_FETCH_MAX:
case BUILTIN_ATOMIC_FETCH_MIN:
case BUILTIN_ATOMIC_FETCH_SUB:
case BUILTIN_ATOMIC_FETCH_DEC_WRAP:
return 5;
case BUILTIN_MEMCOPY:
case BUILTIN_MEMCOPY_INLINE:
case BUILTIN_MEMMOVE:
Expand Down
10 changes: 10 additions & 0 deletions src/compiler/symtab.c
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,16 @@ void symtab_init(uint32_t capacity)
builtin_list[BUILTIN_ABS] = KW_DEF("abs");
builtin_list[BUILTIN_ATOMIC_LOAD] = KW_DEF("atomic_load");
builtin_list[BUILTIN_ATOMIC_STORE] = KW_DEF("atomic_store");
builtin_list[BUILTIN_ATOMIC_FETCH_ADD] = KW_DEF("atomic_fetch_add");
builtin_list[BUILTIN_ATOMIC_FETCH_SUB] = KW_DEF("atomic_fetch_sub");
builtin_list[BUILTIN_ATOMIC_FETCH_MAX] = KW_DEF("atomic_fetch_max");
builtin_list[BUILTIN_ATOMIC_FETCH_MIN] = KW_DEF("atomic_fetch_min");
builtin_list[BUILTIN_ATOMIC_FETCH_AND] = KW_DEF("atomic_fetch_and");
builtin_list[BUILTIN_ATOMIC_FETCH_NAND] = KW_DEF("atomic_fetch_nand");
builtin_list[BUILTIN_ATOMIC_FETCH_OR] = KW_DEF("atomic_fetch_or");
builtin_list[BUILTIN_ATOMIC_FETCH_XOR] = KW_DEF("atomic_fetch_xor");
builtin_list[BUILTIN_ATOMIC_FETCH_INC_WRAP] = KW_DEF("atomic_fetch_inc_wrap");
builtin_list[BUILTIN_ATOMIC_FETCH_DEC_WRAP] = KW_DEF("atomic_fetch_dec_wrap");
builtin_list[BUILTIN_BITREVERSE] = KW_DEF("bitreverse");
builtin_list[BUILTIN_BSWAP] = KW_DEF("bswap");
builtin_list[BUILTIN_CEIL] = KW_DEF("ceil");
Expand Down
2 changes: 1 addition & 1 deletion src/version.h
Original file line number Diff line number Diff line change
@@ -1 +1 @@
#define COMPILER_VERSION "0.4.646"
#define COMPILER_VERSION "0.4.647"

0 comments on commit f6e18de

Please sign in to comment.