From fb56d380cc90ceb17b9a032f53d92e4521adf04f Mon Sep 17 00:00:00 2001 From: Christoffer Lerno Date: Sat, 13 Nov 2021 11:34:52 +0100 Subject: [PATCH] Builtins work. Math library exposes some builtins. Volatile store / load. --- resources/lib/std/atomic.c3 | 2 + resources/lib/std/math.c3 | 192 ++++------------- resources/lib/std/mem.c3 | 10 + src/compiler/compiler_internal.h | 22 +- src/compiler/enums.h | 35 +++- src/compiler/lexer.c | 2 +- src/compiler/linker.c | 2 +- src/compiler/llvm_codegen.c | 27 ++- src/compiler/llvm_codegen_expr.c | 126 ++++++++---- src/compiler/llvm_codegen_internal.h | 8 +- src/compiler/parse_expr.c | 7 +- src/compiler/sema_decls.c | 1 + src/compiler/sema_expr.c | 205 +++++++++++++++++-- src/compiler/sema_passes.c | 16 -- src/compiler/symtab.c | 45 ++++ src/compiler/types.c | 7 + src/version.h | 2 +- test/test_suite/builtins/simple_builtins.c3t | 57 ++++++ 18 files changed, 516 insertions(+), 250 deletions(-) create mode 100644 resources/lib/std/atomic.c3 create mode 100644 test/test_suite/builtins/simple_builtins.c3t diff --git a/resources/lib/std/atomic.c3 b/resources/lib/std/atomic.c3 new file mode 100644 index 000000000..ea778e589 --- /dev/null +++ b/resources/lib/std/atomic.c3 @@ -0,0 +1,2 @@ +module std::atomic; + diff --git a/resources/lib/std/math.c3 b/resources/lib/std/math.c3 index 1645a7f1d..7d5c19825 100644 --- a/resources/lib/std/math.c3 +++ b/resources/lib/std/math.c3 @@ -63,171 +63,53 @@ const QUAD_MAX_EXP = 16384; const QUAD_MIN_EXP = -16481; const QUAD_EPSILON = 1.92592994438723585305597794258492732e-34; */ -private union DoubleLong + +fn double log10(double x) @inline { - double f; - ulong i; + return $$log10(x); } -func double log10(double x) +fn double log2(double x) @inline { - const double IVLN10HI = 4.34294481878168880939e-01; /* 0x3fdbcb7b, 0x15200000 */ - const double IVLN10LO = 2.50829467116452752298e-11; /* 0x3dbb9438, 0xca9aadd5 */ - const double LOG10_2HI = 3.01029995663611771306e-01; /* 0x3FD34413, 0x509F6000 */ - const double LOG10_2LO = 3.69423907715893078616e-13; /* 0x3D59FEF3, 0x11F12B36 */ - const double LG1 = 6.666666666666735130e-01; /* 3FE55555 55555593 */ - const double LG2 = 3.999999999940941908e-01; /* 3FD99999 9997FA04 */ - const double LG3 = 2.857142874366239149e-01; /* 3FD24924 94229359 */ - const double LG4 = 2.222219843214978396e-01; /* 3FCC71C5 1D8E78AF */ - const double LG5 = 1.818357216161805012e-01; /* 3FC74664 96CB03DE */ - const double LG6 = 1.531383769920937332e-01; /* 3FC39A09 D078C69F */ - const double LG7 = 1.479819860511658591e-01; /* 3FC2F112 DF3E5244 */ - DoubleLong u = { .f = x }; - ulong hx = (uint)(u.i >> 32); - int k = 0; - if (hx < 0x00100000 || hx >> 31) - { - if (u.i << 1 == 0) return -1 / (x * x); /* log(+-0)=-inf */ - if (hx >> 31) return (x - x) / 0.0; /* log(-#) = NaN */ - /* subnormal number, scale x up */ - k -= 54; - x *= 0x1p54; - u.f = x; - hx = (uint)(u.i >> 32); - } - else if (hx >= 0x7ff00000) - { - return x; - } - else if (hx == 0x3ff00000 && u.i << 32 == 0) - { - return 0; - } - /* reduce x into [sqrt(2)/2, sqrt(2)] */ - hx += 0x3ff00000 - 0x3fe6a09e; - k += (int)(hx >> 20) - 0x3ff; - hx = (hx & 0x000fffff) + 0x3fe6a09e; - u.i = (ulong)(hx << 32) | (u.i & 0xffffffffu64); - x = u.f; - - hx += 0x3ff00000 - 0x3fe6a09e; - k += (int)(hx >> 20) - 0x3ff; - hx = (hx & 0x000fffff) + 0x3fe6a09e; - u.i = (ulong)(hx << 32) | (u.i & 0xffffffffu64); - x = u.f; - - - double f = x - 1.0; - double hfsq = 0.5 * f * f; - double s = f / (2.0+f); - double z = s * s; - double w = z * z; - double t1 = w * (LG2 + w * (LG4 + w * LG6)); - double t2 = z * (LG1 + w * (LG3 + w * (LG5 + w * LG7))); - double r = t2 + t1; - - /* See log2.c for details. */ - /* hi+lo = f - hfsq + s*(hfsq+R) ~ log(1+f) */ - double hi = f - hfsq; - u.f = hi; - // u.i &= (ulong)(-1) << 32; - u.i &= 0xFFFFFFFF00000000u64; - hi = u.f; - double lo = f - hi - hfsq + s * (hfsq + r); - - /* val_hi+val_lo ~ log10(1+f) + k*log10(2) */ - double val_hi = hi * IVLN10HI; - double dk = k; - double y = dk * LOG10_2HI; - double val_lo = dk * LOG10_2LO + (lo + hi) * IVLN10LO + lo*IVLN10HI; - - /* - * Extra precision in for adding y is not strictly needed - * since there is no very large cancellation near x = sqrt(2) or - * x = 1/sqrt(2), but we do it anyway since it costs little on CPUs - * with some parallelism and it reduces the error for many args. - */ - w = y + val_hi; - val_lo += (y - w) + val_hi; - val_hi = w; - - return val_lo + val_hi; + return $$log2(x); } -func double cos_limited(double x, double y) +fn double log(double x) @inline { - const double C1 = 4.16666666666666019037e-02; /* 0x3FA55555, 0x5555554C */ - const double C2 = -1.38888888888741095749e-03; /* 0xBF56C16C, 0x16C15177 */ - const double C3 = 2.48015872894767294178e-05; /* 0x3EFA01A0, 0x19CB1590 */ - const double C4 = -2.75573143513906633035e-07; /* 0xBE927E4F, 0x809C52AD */ - const double C5 = 2.08757232129817482790e-09; /* 0x3E21EE9E, 0xBDB4B1C4 */ - const double C6 = -1.13596475577881948265e-11; /* 0xBDA8FAE9, 0xBE8838D4 */ - - double z = x * x; - double w = z * z; - double r = z * (C1+ z * (C2 + z * C3)) + w * w * (C4 + z * (C5 + z * C6)); - double hz = 0.5 * z; - w = 1.0 - hz; - return w + (((1.0 - w) - hz) + (z*r - x*y)); + return $$log(x); } -private func double sin_limited(double x, double y, bool iy) +fn double cos(double x) @inline { - const double S1 = -1.66666666666666324348e-01; // 0xBFC55555, 0x55555549 - const double S2 = 8.33333333332248946124e-03; // 0x3F811111, 0x1110F8A6 - const double S3 = -1.98412698298579493134e-04; // 0xBF2A01A0, 0x19C161D5 - const double S4 = 2.75573137070700676789e-06; // 0x3EC71DE3, 0x57B1FE7D - const double S5 = -2.50507602534068634195e-08; // 0xBE5AE5E6, 0x8A2B9CEB - const double S6 = 1.58969099521155010221e-10; // 0x3DE5D93A, 0x5ACFD57C - - double z = x * x; - double w = z * z; - double r = S2 + z * (S3 + z * S4) + z * w * (S5 + z * S6); - double v = z * x; - if (!iy) - { - return x + v * (S1 + z * r); - } - else - { - return x - ((z * (0.5 * y - v * r) - y) - v * S1); - } + return $$cos(x); } -/* -public func double cos(double x) + +fn double sin(double x) @inline { - double[2] y; - uint32_t ix; - unsigned n; - - GET_HIGH_WORD(ix, x); - ix &= 0x7fffffff; - - /* |x| ~< pi/4 */ - if (ix <= 0x3fe921fb) - { - if (ix < 0x3e46a09e) - { - // |x| < 2**-27 * sqrt(2) - /* raise inexact if x!=0 */ - FORCE_EVAL(x + 0x1p120f); - return 1.0; - } - return cos_limited(x, 0); - } - - /* cos(Inf or NaN) is NaN */ - if (ix >= 0x7ff00000) return x - x; - - /* argument reduction */ - n = __rem_pio2(x, y); - switch (n&3) - { - case 0: return cos_limited(y[0], y[1]); - case 1: return -sin_limited(y[0], y[1], true); - case 2: return -cos_limited(y[0], y[1]); - default: - return sin_limited(y[0], y[1], true); - } + return $$sin(x); +} + +fn double exp(double x) @inline +{ + return $$exp(x); +} + +fn double pow(double x, double y) @inline +{ + return $$pow(x, y); +} + +fn double fabs(double x) @inline +{ + return $$fabs(x); +} + +fn double trunc(double x) @inline +{ + return $$trunc(x); +} + +fn double ceil(double x) @inline +{ + return $$ceil(x); } -*/ \ No newline at end of file diff --git a/resources/lib/std/mem.c3 b/resources/lib/std/mem.c3 index fc5a02e5c..044be0b9e 100644 --- a/resources/lib/std/mem.c3 +++ b/resources/lib/std/mem.c3 @@ -5,6 +5,16 @@ extern func void* _realloc(void* ptr, usize bytes) @extname("realloc"); extern func void* _calloc(usize bytes, usize elements) @extname("calloc"); extern func void _free(void* ptr) @extname("free"); +macro volatile_load(&x) +{ + return $$volatile_load(&x); +} + +macro volatile_store(&x, y) +{ + return $$volatile_store(&x, y); +} + enum AllocationKind { ALLOC, diff --git a/src/compiler/compiler_internal.h b/src/compiler/compiler_internal.h index 3af171fba..688de084b 100644 --- a/src/compiler/compiler_internal.h +++ b/src/compiler/compiler_internal.h @@ -450,7 +450,6 @@ typedef struct { struct { - bool is_builtin : 1; bool attr_weak : 1; bool attr_noreturn : 1; bool attr_inline : 1; @@ -684,6 +683,7 @@ typedef struct bool unsplat_last : 1; bool force_inline : 1; bool force_noinline : 1; + bool is_builtin : 1; union { Expr *function; @@ -911,6 +911,7 @@ typedef struct typedef struct { Token identifier; + BuiltinFunction builtin; } ExprBuiltin; struct Expr_ { @@ -1529,7 +1530,7 @@ extern Type *type_complist; extern Type *type_anyfail; extern const char *attribute_list[NUMBER_OF_ATTRIBUTES]; - +extern const char *builtin_list[NUMBER_OF_BUILTINS]; extern const char *kw_std; extern const char *kw_max; extern const char *kw_min; @@ -1562,6 +1563,22 @@ extern const char *kw_LINEREAL; extern const char *kw_default_iterator; extern const char *kw_incr; extern const char *kw_check_assign; +extern const char *kw_builtin_ceil; +extern const char *kw_builtin_trunc; +extern const char *kw_builtin_sqrt; +extern const char *kw_builtin_cos; +extern const char *kw_builtin_sin; +extern const char *kw_builtin_log; +extern const char *kw_builtin_log2; +extern const char *kw_builtin_log10; +extern const char *kw_builtin_max; +extern const char *kw_builtin_min; +extern const char *kw_builtin_pow; +extern const char *kw_builtin_exp; +extern const char *kw_builtin_fabs; +extern const char *kw_builtin_fma; +extern const char *kw_builtin_cmpxchg; + #define AST_NEW_TOKEN(_kind, _token) new_ast(_kind, source_span_from_token_id((_token).id)) #define AST_NEW(_kind, _loc) new_ast(_kind, _loc) @@ -2032,6 +2049,7 @@ bool type_is_user_defined(Type *type); bool type_is_structurally_equivalent(Type *type1, Type *type); static inline Type *type_flatten(Type *type); static inline bool type_is_vector(Type *type) { return type_flatten(type)->type_kind == TYPE_VECTOR; }; +bool type_is_float_or_float_vector(Type *type); bool type_may_have_sub_elements(Type *type); static inline bool type_ok(Type *type); ByteSize type_size(Type *type); diff --git a/src/compiler/enums.h b/src/compiler/enums.h index a932396b3..e9fc77fc0 100644 --- a/src/compiler/enums.h +++ b/src/compiler/enums.h @@ -675,4 +675,37 @@ typedef enum ANALYSIS_CT_ASSERT, ANALYSIS_FUNCTIONS, ANALYSIS_LAST = ANALYSIS_FUNCTIONS -} AnalysisStage; \ No newline at end of file +} AnalysisStage; + +typedef enum +{ + BUILTIN_CEIL, + BUILTIN_TRUNC, + BUILTIN_SQRT, + BUILTIN_COS, + BUILTIN_SIN, + BUILTIN_LOG, + BUILTIN_LOG2, + BUILTIN_LOG10, + BUILTIN_MAX, + BUILTIN_MIN, + BUILTIN_POW, + BUILTIN_EXP, + BUILTIN_FABS, + BUILTIN_FMA, + BUILTIN_VOLATILE_LOAD, + BUILTIN_VOLATILE_STORE, + BUILTIN_NONE, + NUMBER_OF_BUILTINS = BUILTIN_NONE, +} BuiltinFunction; + +typedef enum +{ + ATOMIC_NONE, + ATOMIC_UNORDERED, + ATOMIC_RELAXED, + ATOMIC_ACQUIRE, + ATOMIC_RELEASE, + ATOMIC_ACQUIRE_RELEASE, + ATOMIC_SEQ_CONSISTENT, +} Atomicity; diff --git a/src/compiler/lexer.c b/src/compiler/lexer.c index 7a5614a62..78e7b0ded 100644 --- a/src/compiler/lexer.c +++ b/src/compiler/lexer.c @@ -3,7 +3,6 @@ // a copy of which can be found in the LICENSE file. #include "compiler_internal.h" -#include "errno.h" typedef enum { @@ -1626,6 +1625,7 @@ static bool lexer_scan_token_inner(Lexer *lexer, LexMode mode) if (is_letter(peek(lexer))) { add_token(lexer, TOKEN_BUILTIN, "$$"); + lexer->lexing_start = lexer->current; return scan_ident(lexer, TOKEN_IDENT, TOKEN_CONST_IDENT, TOKEN_TYPE_IDENT, 0); } return add_error_token(lexer, "Expected a letter after $$."); diff --git a/src/compiler/linker.c b/src/compiler/linker.c index e24e5fb4c..b6dd59f1b 100644 --- a/src/compiler/linker.c +++ b/src/compiler/linker.c @@ -280,7 +280,6 @@ void platform_linker(const char *output_file, const char **files, unsigned file_ { const char **parts = NULL; vec_add(parts, "cc"); - vec_add(parts, "-lm"); VECEACH(active_target.link_args, i) { vec_add(parts, active_target.link_args[i]); @@ -306,6 +305,7 @@ void platform_linker(const char *output_file, const char **files, unsigned file_ { vec_add(parts, files[i]); } + vec_add(parts, "-lm"); const char *output = concat_string_parts(parts); if (system(output) != 0) { diff --git a/src/compiler/llvm_codegen.c b/src/compiler/llvm_codegen.c index 0952768a5..f1f15ff48 100644 --- a/src/compiler/llvm_codegen.c +++ b/src/compiler/llvm_codegen.c @@ -547,7 +547,6 @@ unsigned intrinsic_id_smul_overflow; unsigned intrinsic_id_umul_overflow; unsigned intrinsic_id_sshl_sat; unsigned intrinsic_id_ushl_sat; -unsigned intrinsic_id_fmuladd; unsigned intrinsic_id_rint; unsigned intrinsic_id_trunc; unsigned intrinsic_id_ceil; @@ -566,6 +565,7 @@ unsigned intrinsic_id_cos; unsigned intrinsic_id_exp; unsigned intrinsic_id_exp2; unsigned intrinsic_id_log; +unsigned intrinsic_id_log2; unsigned intrinsic_id_log10; unsigned intrinsic_id_fabs; unsigned intrinsic_id_fma; @@ -654,6 +654,7 @@ void llvm_codegen_setup() intrinsic_id_exp = lookup_intrinsic("llvm.exp"); intrinsic_id_exp2 = lookup_intrinsic("llvm.exp2"); intrinsic_id_log = lookup_intrinsic("llvm.log"); + intrinsic_id_log2 = lookup_intrinsic("llvm.log2"); intrinsic_id_log10 = lookup_intrinsic("llvm.log10"); intrinsic_id_fabs = lookup_intrinsic("llvm.fabs"); intrinsic_id_fma = lookup_intrinsic("llvm.fma"); @@ -1149,7 +1150,7 @@ AlignSize llvm_abi_alignment(GenContext *c, LLVMTypeRef type) return (AlignSize)LLVMABIAlignmentOfType(c->target_data, type); } -void llvm_store_bevalue_aligned(GenContext *c, LLVMValueRef destination, BEValue *value, AlignSize alignment) +LLVMValueRef llvm_store_bevalue_aligned(GenContext *c, LLVMValueRef destination, BEValue *value, AlignSize alignment) { // If we have an address but not an aggregate, do a load. llvm_value_fold_failable(c, value); @@ -1165,8 +1166,7 @@ void llvm_store_bevalue_aligned(GenContext *c, LLVMValueRef destination, BEValue value->kind = BE_VALUE; FALLTHROUGH; case BE_VALUE: - llvm_store_aligned(c, destination, value->value, alignment ? alignment : type_abi_alignment(value->type)); - return; + return llvm_store_aligned(c, destination, value->value, alignment ? alignment : type_abi_alignment(value->type)); case BE_ADDRESS_FAILABLE: UNREACHABLE case BE_ADDRESS: @@ -1176,9 +1176,13 @@ void llvm_store_bevalue_aligned(GenContext *c, LLVMValueRef destination, BEValue LLVMValueRef copy_size = llvm_const_int(c, size <= UINT32_MAX ? type_uint : type_usize, size); destination = LLVMBuildBitCast(c->builder, destination, llvm_get_ptr_type(c, type_char), ""); LLVMValueRef source = LLVMBuildBitCast(c->builder, value->value, llvm_get_ptr_type(c, type_char), ""); - LLVMBuildMemCpy(c->builder, destination, alignment ? alignment : type_abi_alignment(value->type), - source, value->alignment ? value->alignment : type_abi_alignment(value->type), copy_size); - return; + LLVMValueRef copy = LLVMBuildMemCpy(c->builder, + destination, + alignment ? alignment : type_abi_alignment(value->type), + source, + value->alignment ? value->alignment : type_abi_alignment(value->type), + copy_size); + return copy; } } UNREACHABLE @@ -1189,11 +1193,11 @@ void llvm_store_bevalue_dest_aligned(GenContext *c, LLVMValueRef destination, BE llvm_store_bevalue_aligned(c, destination, value, LLVMGetAlignment(destination)); } -void llvm_store_bevalue(GenContext *c, BEValue *destination, BEValue *value) +LLVMValueRef llvm_store_bevalue(GenContext *c, BEValue *destination, BEValue *value) { - if (value->type == type_void) return; + if (value->type == type_void) return NULL; assert(llvm_value_is_addr(destination)); - llvm_store_bevalue_aligned(c, destination->value, value, destination->alignment); + return llvm_store_bevalue_aligned(c, destination->value, value, destination->alignment); } void llvm_store_bevalue_raw(GenContext *c, BEValue *destination, LLVMValueRef raw_value) @@ -1207,10 +1211,11 @@ void llvm_store_self_aligned(GenContext *context, LLVMValueRef pointer, LLVMValu llvm_store_aligned(context, pointer, value, type_abi_alignment(type)); } -void llvm_store_aligned(GenContext *context, LLVMValueRef pointer, LLVMValueRef value, AlignSize alignment) +LLVMValueRef llvm_store_aligned(GenContext *context, LLVMValueRef pointer, LLVMValueRef value, AlignSize alignment) { LLVMValueRef ref = LLVMBuildStore(context->builder, value, pointer); if (alignment) llvm_set_alignment(ref, alignment); + return ref; } void llvm_store_aligned_decl(GenContext *context, Decl *decl, LLVMValueRef value) diff --git a/src/compiler/llvm_codegen_expr.c b/src/compiler/llvm_codegen_expr.c index dfdb64edf..8a3bf686d 100644 --- a/src/compiler/llvm_codegen_expr.c +++ b/src/compiler/llvm_codegen_expr.c @@ -3889,42 +3889,23 @@ void llvm_value_struct_gep(GenContext *c, BEValue *element, BEValue *struct_poin element->alignment = alignment; } -static void llvm_emit_fp_intrinsic_expr(GenContext *c, unsigned intrinsic_id, BEValue *be_value, Expr *expr) +static void llvm_emit_intrinsic_expr(GenContext *c, unsigned intrinsic_id, BEValue *be_value, Expr *expr) { unsigned arguments = vec_size(expr->call_expr.arguments); - llvm_emit_expr(c, be_value, expr->call_expr.arguments[0]); - llvm_value_rvalue(c, be_value); - LLVMTypeRef call_type = llvm_get_type(c, be_value->type); - LLVMValueRef result = llvm_emit_call_intrinsic(c, intrinsic_id, &call_type, 1, &be_value->value, 1); - be_value->value = result; -} -void gencontext_emit_call_intrinsic_expr(GenContext *c, BEValue *be_value, Expr *expr) -{ - Decl *function_decl = expr->call_expr.function->identifier_expr.decl; - function_decl = decl_flatten(function_decl); - if (function_decl->name == kw___round) - { - llvm_emit_fp_intrinsic_expr(c, intrinsic_id_rint, be_value, expr); - return; - } - if (function_decl->name == kw___sqrt) - { - llvm_emit_fp_intrinsic_expr(c, intrinsic_id_sqrt, be_value, expr); - return; - } - if (function_decl->name == kw___trunc) - { - llvm_emit_fp_intrinsic_expr(c, intrinsic_id_trunc, be_value, expr); - return; - } - if (function_decl->name == kw___ceil) - { - llvm_emit_fp_intrinsic_expr(c, intrinsic_id_ceil, be_value, expr); - return; - } - UNREACHABLE + assert(arguments < 5 && "Only has room for 4"); + LLVMValueRef arg_results[4]; + for (unsigned i = 0; i < arguments; i++) + { + llvm_emit_expr(c, be_value, expr->call_expr.arguments[0]); + llvm_value_rvalue(c, be_value); + arg_results[i] = be_value->value; + } + LLVMTypeRef call_type = llvm_get_type(c, expr->type); + LLVMValueRef result = llvm_emit_call_intrinsic(c, intrinsic_id, &call_type, 1, arg_results, arguments); + llvm_value_set(be_value, result, expr->type); } + void llvm_emit_parameter(GenContext *c, LLVMValueRef **args, ABIArgInfo *info, BEValue *be_value, Type *type) { type = type_lowering(type); @@ -4059,8 +4040,83 @@ static void llvm_emit_unpacked_variadic_arg(GenContext *c, Expr *expr, BEValue * } } +unsigned llvm_get_intrinsic(BuiltinFunction func) +{ + switch (func) + { + case BUILTIN_NONE: + UNREACHABLE + case BUILTIN_CEIL: return intrinsic_id_ceil; + case BUILTIN_TRUNC: return intrinsic_id_trunc; + case BUILTIN_SQRT: return intrinsic_id_sqrt; + case BUILTIN_COS: return intrinsic_id_cos; + case BUILTIN_SIN: return intrinsic_id_sin; + case BUILTIN_LOG: return intrinsic_id_log; + case BUILTIN_LOG10: return intrinsic_id_log10; + case BUILTIN_MAX: return intrinsic_id_maxnum; + case BUILTIN_MIN: return intrinsic_id_minnum; + case BUILTIN_FABS: return intrinsic_id_fabs; + case BUILTIN_FMA: return intrinsic_id_fma; + case BUILTIN_LOG2: return intrinsic_id_log2; + case BUILTIN_POW: return intrinsic_id_pow; + case BUILTIN_EXP: return intrinsic_id_exp; + case BUILTIN_VOLATILE_STORE: + case BUILTIN_VOLATILE_LOAD: + UNREACHABLE + } + UNREACHABLE +} + +LLVMAtomicOrdering llvm_atomic_ordering(Atomicity atomicity) +{ + switch (atomicity) + { + case ATOMIC_NONE: return LLVMAtomicOrderingNotAtomic; + case ATOMIC_UNORDERED: return LLVMAtomicOrderingUnordered; + case ATOMIC_RELAXED: return LLVMAtomicOrderingMonotonic; + case ATOMIC_ACQUIRE: return LLVMAtomicOrderingAcquire; + case ATOMIC_RELEASE: return LLVMAtomicOrderingRelease; + case ATOMIC_ACQUIRE_RELEASE: return LLVMAtomicOrderingAcquireRelease; + case ATOMIC_SEQ_CONSISTENT: return LLVMAtomicOrderingSequentiallyConsistent; + } + UNREACHABLE +} + +void llvm_emit_builtin_call(GenContext *c, BEValue *result_value, Expr *expr) +{ + BuiltinFunction func = expr->call_expr.function->builtin_expr.builtin; + if (func == BUILTIN_VOLATILE_STORE) + { + 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); + value.kind = BE_ADDRESS; + BEValue store_value = *result_value; + LLVMValueRef store = llvm_store_bevalue(c, &value, &store_value); + if (store) LLVMSetVolatile(store, true); + return; + } + if (func == BUILTIN_VOLATILE_LOAD) + { + llvm_emit_expr(c, result_value, expr->call_expr.arguments[0]); + llvm_value_rvalue(c, result_value); + result_value->kind = BE_ADDRESS; + result_value->type = type_lowering(result_value->type->pointer); + llvm_value_rvalue(c, result_value); + LLVMSetVolatile(result_value->value, true); + return; + } + llvm_emit_intrinsic_expr(c, llvm_get_intrinsic(func), result_value, expr); +} + void llvm_emit_call_expr(GenContext *c, BEValue *result_value, Expr *expr) { + if (expr->call_expr.is_builtin) + { + llvm_emit_builtin_call(c, result_value, expr); + return; + } FunctionSignature *signature; LLVMTypeRef func_type; LLVMValueRef func; @@ -4092,13 +4148,9 @@ void llvm_emit_call_expr(GenContext *c, BEValue *result_value, Expr *expr) else { // 2a. Get the function declaration + Decl *function_decl = expr->call_expr.func_ref; always_inline = function_decl->func_decl.attr_inline; - if (function_decl->func_decl.is_builtin) - { - gencontext_emit_call_intrinsic_expr(c, result_value, expr); - return; - } // 2b. Set signature, function and function type signature = &function_decl->func_decl.function_signature; diff --git a/src/compiler/llvm_codegen_internal.h b/src/compiler/llvm_codegen_internal.h index 63e8be41c..39c98cec2 100644 --- a/src/compiler/llvm_codegen_internal.h +++ b/src/compiler/llvm_codegen_internal.h @@ -117,7 +117,6 @@ extern unsigned intrinsic_id_umul_overflow; extern unsigned intrinsic_id_trap; extern unsigned intrinsic_id_bswap; extern unsigned intrinsic_id_assume; -extern unsigned intrinsic_id_fmuladd; extern unsigned intrinsic_id_rint; extern unsigned intrinsic_id_trunc; extern unsigned intrinsic_id_ceil; @@ -136,6 +135,7 @@ extern unsigned intrinsic_id_cos; extern unsigned intrinsic_id_exp; extern unsigned intrinsic_id_exp2; extern unsigned intrinsic_id_log; +extern unsigned intrinsic_id_log2; extern unsigned intrinsic_id_log10; extern unsigned intrinsic_id_fabs; extern unsigned intrinsic_id_fma; @@ -306,12 +306,12 @@ bool llvm_emit_check_block_branch(GenContext *context); unsigned llvm_store_size(GenContext *c, LLVMTypeRef type); -void llvm_store_bevalue(GenContext *c, BEValue *destination, BEValue *value); +LLVMValueRef llvm_store_bevalue(GenContext *c, BEValue *destination, BEValue *value); void llvm_store_bevalue_raw(GenContext *c, BEValue *destination, LLVMValueRef raw_value); void llvm_store_bevalue_dest_aligned(GenContext *c, LLVMValueRef destination, BEValue *value); -void llvm_store_bevalue_aligned(GenContext *c, LLVMValueRef destination, BEValue *value, AlignSize alignment); +LLVMValueRef llvm_store_bevalue_aligned(GenContext *c, LLVMValueRef destination, BEValue *value, AlignSize alignment); void llvm_store_self_aligned(GenContext *context, LLVMValueRef pointer, LLVMValueRef value, Type *type); -void llvm_store_aligned(GenContext *context, LLVMValueRef pointer, LLVMValueRef value, AlignSize alignment); +LLVMValueRef llvm_store_aligned(GenContext *context, LLVMValueRef pointer, LLVMValueRef value, AlignSize alignment); void llvm_store_aligned_decl(GenContext *context, Decl *decl, LLVMValueRef value); LLVMTypeRef llvm_get_twostruct(GenContext *context, LLVMTypeRef lo, LLVMTypeRef hi); diff --git a/src/compiler/parse_expr.c b/src/compiler/parse_expr.c index 998b90ad2..003631539 100644 --- a/src/compiler/parse_expr.c +++ b/src/compiler/parse_expr.c @@ -893,10 +893,7 @@ static Expr *parse_force_unwrap_expr(Context *context, Expr *left) static Expr *parse_or_error_expr(Context *context, Expr *left) { Expr *else_expr = EXPR_NEW_TOKEN(EXPR_OR_ERROR, context->tok); - if (!try_consume(context, TOKEN_ELSE)) - { - advance_and_verify(context, TOKEN_QUESTQUEST); - } + advance_and_verify(context, TOKEN_QUESTQUEST); else_expr->or_error_expr.expr = left; switch (context->tok.type) { @@ -1612,7 +1609,7 @@ ParseRule rules[TOKEN_EOF + 1] = { [TOKEN_CT_CONST_IDENT] = { parse_ct_ident, NULL, PREC_NONE }, [TOKEN_CT_TYPE_IDENT] = { parse_type_identifier, NULL, PREC_NONE }, [TOKEN_HASH_IDENT] = { parse_hash_ident, NULL, PREC_NONE }, - //[TOKEN_HASH_TYPE_IDENT] = { parse_type_identifier(, NULL, PREC_NONE } + //[TOKEN_HASH_TYPE_IDENT] = { parse_type_identifier, NULL, PREC_NONE } [TOKEN_CT_SIZEOF] = { parse_ct_call, NULL, PREC_NONE }, [TOKEN_CT_ALIGNOF] = { parse_ct_call, NULL, PREC_NONE }, diff --git a/src/compiler/sema_decls.c b/src/compiler/sema_decls.c index 2d2158db1..934e620f1 100644 --- a/src/compiler/sema_decls.c +++ b/src/compiler/sema_decls.c @@ -909,6 +909,7 @@ static inline AttributeType attribute_by_name(Attr *attr) return ATTRIBUTE_NONE; } + static const char *attribute_domain_to_string(AttributeDomain domain) { switch (domain) diff --git a/src/compiler/sema_expr.c b/src/compiler/sema_expr.c index 8a029bbd5..33621f699 100644 --- a/src/compiler/sema_expr.c +++ b/src/compiler/sema_expr.c @@ -1015,14 +1015,6 @@ static inline bool sema_expr_analyse_intrinsic_fp_invocation(Context *context, E } -static inline bool sema_expr_analyse_intrinsic_invocation(Context *context, Expr *expr, Decl *decl, bool *failable) -{ - if (decl->name == kw___ceil || decl->name == kw___trunc || decl->name == kw___round || decl->name == kw___sqrt) - { - return sema_expr_analyse_intrinsic_fp_invocation(context, expr, decl, failable); - } - UNREACHABLE -} static inline bool expr_may_unpack_as_vararg(Expr *expr, Type *variadic_base_type) { @@ -1435,13 +1427,6 @@ static inline bool sema_expr_analyse_func_invocation(Context *context, FunctionS }; if (!sema_expr_analyse_call_invocation(context, expr, callee, &failable)) return false; - // 2. Builtin? We handle that elsewhere. - if (decl && decl->func_decl.is_builtin) - { - assert(!struct_var); - return sema_expr_analyse_intrinsic_invocation(context, expr, decl, &failable); - } - Type *rtype = signature->rtype->type; expr->type = type_get_opt_fail(rtype, failable); @@ -1973,6 +1958,168 @@ bool sema_expr_analyse_general_call(Context *context, Expr *expr, Decl *decl, Ex } + +static inline unsigned builtin_expected_args(BuiltinFunction func) +{ + switch (func) + { + case BUILTIN_CEIL: + case BUILTIN_TRUNC: + case BUILTIN_SQRT: + case BUILTIN_COS: + case BUILTIN_SIN: + case BUILTIN_EXP: + case BUILTIN_LOG: + case BUILTIN_LOG2: + case BUILTIN_LOG10: + case BUILTIN_FABS: + case BUILTIN_VOLATILE_LOAD: + return 1; + case BUILTIN_POW: + case BUILTIN_MAX: + case BUILTIN_MIN: + case BUILTIN_VOLATILE_STORE: + return 2; + case BUILTIN_FMA: + return 3; + case BUILTIN_NONE: + UNREACHABLE + } + UNREACHABLE +} +static inline bool sema_expr_analyse_builtin_call(Context *context, Expr *expr) +{ + expr->call_expr.is_builtin = true; + BuiltinFunction func = expr->call_expr.function->builtin_expr.builtin; + unsigned expected_args = builtin_expected_args(func); + Expr **args = expr->call_expr.arguments; + unsigned arg_count = vec_size(args); + + // 1. Handle arg count, so at least we know that is ok. + if (expected_args != arg_count) + { + if (arg_count == 0) + { + SEMA_ERROR(expr, "Expected %d arguments to builtin.\n", expected_args); + return false; + } + if (arg_count < expected_args) + { + SEMA_ERROR(args[arg_count - 1], "Expected more arguments after this one."); + return false; + } + SEMA_ERROR(args[expected_args], "Too many arguments."); + return false; + } + + bool failable = false; + + // 2. We can now check all the arguments, since they in general work on the + // exact type size, we don't do any forced promotion. + for (unsigned i = 0; i < arg_count; i++) + { + if (!sema_analyse_expr(context, args[i])) return false; + failable = failable || type_is_failable(args[i]->type); + } + + Type *rtype = NULL; + switch (func) + { + case BUILTIN_CEIL: + case BUILTIN_TRUNC: + case BUILTIN_SQRT: + case BUILTIN_COS: + case BUILTIN_SIN: + case BUILTIN_POW: + case BUILTIN_EXP: + case BUILTIN_FABS: + case BUILTIN_LOG: + case BUILTIN_LOG2: + case BUILTIN_LOG10: + if (type_is_float_or_float_vector(args[0]->type)) + { + rtype = args[0]->type; + break; + } + SEMA_ERROR(args[0], "Expected a floating point or floating point array."); + return false; + case BUILTIN_MAX: + case BUILTIN_MIN: + if (!type_is_float_or_float_vector(args[0]->type)) + { + SEMA_ERROR(args[0], "Expected a floating point or floating point array."); + return false; + } + if (!type_is_float_or_float_vector(args[1]->type)) + { + SEMA_ERROR(args[1], "Expected a floating point or floating point array."); + return false; + } + if (type_flatten(args[0]->type) != type_flatten(args[1]->type)) + { + SEMA_ERROR(args[1], "Expected an expression of type %s.", type_quoted_error_string(args[0]->type)); + return false; + } + rtype = args[0]->type; + break; + case BUILTIN_FMA: + if (!type_is_float_or_float_vector(args[0]->type)) + { + SEMA_ERROR(args[0], "Expected a floating point or floating point array."); + return false; + } + if (!type_is_float_or_float_vector(args[1]->type)) + { + SEMA_ERROR(args[1], "Expected a floating point or floating point array."); + return false; + } + if (!type_is_float_or_float_vector(args[2]->type)) + { + SEMA_ERROR(args[2], "Expected a floating point or floating point array."); + return false; + } + if (type_flatten(args[0]->type) != type_flatten(args[1]->type)) + { + SEMA_ERROR(args[1], "Expected an expression of type %s.", type_quoted_error_string(args[0]->type)); + return false; + } + if (type_flatten(args[0]->type) != type_flatten(args[2]->type)) + { + SEMA_ERROR(args[2], "Expected an expression of type %s.", type_quoted_error_string(args[0]->type)); + return false; + } + rtype = args[0]->type; + break; + case BUILTIN_VOLATILE_LOAD: + { + Type *type = type_flatten(args[0]->type); + if (!type_is_pointer(type)) + { + SEMA_ERROR(args[0], "Expected a pointer."); + return false; + } + rtype = type->pointer; + break; + } + case BUILTIN_VOLATILE_STORE: + { + Type *type = type_flatten(args[0]->type); + if (!type_is_pointer(type)) + { + SEMA_ERROR(args[0], "Expected a pointer."); + return false; + } + rtype = type->pointer; + if (!cast_implicit(args[1], rtype)) return false; + break; + } + case BUILTIN_NONE: + UNREACHABLE + } + expr->type = type_get_opt_fail(rtype, failable); + return true; +} + static inline bool sema_expr_analyse_call(Context *context, Expr *expr) { Expr *func_expr = expr->call_expr.function; @@ -1988,6 +2135,8 @@ static inline bool sema_expr_analyse_call(Context *context, Expr *expr) bool macro = false; switch (func_expr->expr_kind) { + case EXPR_BUILTIN: + return sema_expr_analyse_builtin_call(context, expr); case EXPR_IDENTIFIER: decl = func_expr->identifier_expr.decl; break; @@ -6357,6 +6506,30 @@ static inline bool sema_expr_analyse_ct_call(Context *context, Expr *expr) } } +static inline BuiltinFunction builtin_by_name(const char *name) +{ + for (unsigned i = 0; i < NUMBER_OF_BUILTINS; i++) + { + if (builtin_list[i] == name) return (BuiltinFunction)i; + } + return BUILTIN_NONE; +} + +static inline bool sema_expr_analyse_builtin(Context *context, Expr *expr) +{ + const char *builtin_char = TOKSTR(expr->builtin_expr.identifier); + + BuiltinFunction func = builtin_by_name(builtin_char); + + if (func == BUILTIN_NONE) + { + SEMA_TOKEN_ERROR(expr->builtin_expr.identifier, "Unsupported builtin '%s'.", builtin_char); + return false; + } + + expr->builtin_expr.builtin = func; + return true; +} static inline bool sema_analyse_expr_dispatch(Context *context, Expr *expr) { @@ -6377,7 +6550,7 @@ static inline bool sema_analyse_expr_dispatch(Context *context, Expr *expr) expr->type = expr->decl_expr->type; return true; case EXPR_BUILTIN: - TODO + return sema_expr_analyse_builtin(context, expr); case EXPR_CT_CALL: return sema_expr_analyse_ct_call(context, expr); case EXPR_HASH_IDENT: diff --git a/src/compiler/sema_passes.c b/src/compiler/sema_passes.c index 22fb4f454..1d3b58493 100644 --- a/src/compiler/sema_passes.c +++ b/src/compiler/sema_passes.c @@ -4,17 +4,6 @@ #include "sema_internal.h" -void context_add_intrinsic(Context *context, const char *name) -{ - Decl *decl = decl_calloc(); - decl->module = context->module; - decl->decl_kind = DECL_FUNC; - decl->resolve_status = RESOLVE_DONE; - decl->func_decl.is_builtin = true; - decl->name = name; - Decl *old = stable_set(&context->local_symbols, decl->name, decl); - assert(!old); -} void sema_analysis_pass_process_imports(Module *module) { @@ -81,11 +70,6 @@ void sema_analysis_pass_process_imports(Module *module) } } import_count += imports; - // TODO probably remove this: - context_add_intrinsic(context, kw___round); - context_add_intrinsic(context, kw___trunc); - context_add_intrinsic(context, kw___ceil); - context_add_intrinsic(context, kw___sqrt); } (void)import_count; // workaround for clang 13.0 DEBUG_LOG("Pass finished processing %d import(s) with %d error(s).", import_count, global_context.errors_found); diff --git a/src/compiler/symtab.c b/src/compiler/symtab.c index 23e643edf..697ed90e0 100644 --- a/src/compiler/symtab.c +++ b/src/compiler/symtab.c @@ -35,6 +35,7 @@ typedef struct _Entry static SymTab symtab; const char *attribute_list[NUMBER_OF_ATTRIBUTES]; +const char *builtin_list[NUMBER_OF_BUILTINS]; const char *kw_align; const char *kw_deprecated; @@ -68,6 +69,21 @@ const char *kw_LINEREAL; const char *kw_default_iterator; const char *kw_incr; const char *kw_check_assign; +const char *kw_builtin_ceil; +const char *kw_builtin_trunc; +const char *kw_builtin_sqrt; +const char *kw_builtin_cos; +const char *kw_builtin_sin; +const char *kw_builtin_log; +const char *kw_builtin_log2; +const char *kw_builtin_log10; +const char *kw_builtin_max; +const char *kw_builtin_min; +const char *kw_builtin_pow; +const char *kw_builtin_exp; +const char *kw_builtin_fabs; +const char *kw_builtin_fma; +const char *kw_builtin_cmpxchg; void symtab_init(uint32_t capacity) { @@ -132,6 +148,29 @@ void symtab_init(uint32_t capacity) kw_incr = KW_DEF("incr"); kw_default_iterator = KW_DEF("default_iterator"); kw_check_assign = KW_DEF("check_assign"); + + builtin_list[BUILTIN_CEIL] = KW_DEF("ceil"); + builtin_list[BUILTIN_TRUNC] = KW_DEF("trunc"); + builtin_list[BUILTIN_SIN] = KW_DEF("sin"); + builtin_list[BUILTIN_COS] = KW_DEF("cos"); + builtin_list[BUILTIN_LOG] = KW_DEF("log"); + builtin_list[BUILTIN_LOG2] = KW_DEF("log2"); + builtin_list[BUILTIN_LOG10] = KW_DEF("log10"); + builtin_list[BUILTIN_SQRT] = KW_DEF("sqrt"); + builtin_list[BUILTIN_POW] = KW_DEF("pow"); + builtin_list[BUILTIN_EXP] = KW_DEF("exp"); + builtin_list[BUILTIN_MAX] = KW_DEF("max"); + builtin_list[BUILTIN_MIN] = KW_DEF("min"); + builtin_list[BUILTIN_FMA] = KW_DEF("fma"); + builtin_list[BUILTIN_FABS] = KW_DEF("fabs"); + builtin_list[BUILTIN_VOLATILE_STORE] = KW_DEF("volatile_store"); + builtin_list[BUILTIN_VOLATILE_LOAD] = KW_DEF("volatile_load"); + + for (unsigned i = 0; i < NUMBER_OF_BUILTINS; i++) + { + assert(builtin_list[i] && "Missing builtin"); + } + attribute_list[ATTRIBUTE_INLINE] = kw_inline; attribute_list[ATTRIBUTE_NOINLINE] = KW_DEF("noinline"); attribute_list[ATTRIBUTE_OPAQUE] = KW_DEF("opaque"); @@ -152,6 +191,12 @@ void symtab_init(uint32_t capacity) attribute_list[ATTRIBUTE_REGCALL] = KW_DEF("regcall"); attribute_list[ATTRIBUTE_FASTCALL] = KW_DEF("fastcall"); attribute_list[ATTRIBUTE_OVERLAP] = KW_DEF("overlap"); + + for (unsigned i = 0; i < NUMBER_OF_ATTRIBUTES; i++) + { + assert(attribute_list[i] && "Missing attributes"); + } + } static inline SymEntry *entry_find(const char *key, uint32_t key_len, uint32_t hash) diff --git a/src/compiler/types.c b/src/compiler/types.c index 37c0a3fdf..1787306b2 100644 --- a/src/compiler/types.c +++ b/src/compiler/types.c @@ -255,6 +255,13 @@ const char *type_generate_qname(Type *type) } +bool type_is_float_or_float_vector(Type *type) +{ + type = type_flatten(type); + if (type->type_kind == TYPE_VECTOR) type = type->vector.base; + TypeKind kind = type->type_kind; + return kind >= TYPE_FLOAT_FIRST && kind <= TYPE_FLOAT_LAST; +} bool type_is_union_struct(Type *type) { diff --git a/src/version.h b/src/version.h index 43a3c13c8..eda790a8f 100644 --- a/src/version.h +++ b/src/version.h @@ -1 +1 @@ -#define COMPILER_VERSION "A240" \ No newline at end of file +#define COMPILER_VERSION "PRE.1" \ No newline at end of file diff --git a/test/test_suite/builtins/simple_builtins.c3t b/test/test_suite/builtins/simple_builtins.c3t new file mode 100644 index 000000000..83ede8c20 --- /dev/null +++ b/test/test_suite/builtins/simple_builtins.c3t @@ -0,0 +1,57 @@ +// #target: x64-darwin +module foo; + +fn int foo(double b) +{ + double d = $$ceil(b); + double e = $$max(1.0, d); + double f = $$fma(d, 2.0, 3.0); + int xeb = 13; + + int[3] abcd; + int sy = $$volatile_load(&xeb); + $$volatile_store(&xeb, sy + 1); + $$volatile_store(&abcd[2], sy + 2); + sy = $$volatile_load(&abcd[2]); + return 1; +} + +// #expect: foo.ll + +define i32 @foo.foo(double %0) #0 { +entry: + %b = alloca double, align 8 + %d = alloca double, align 8 + %e = alloca double, align 8 + %f = alloca double, align 8 + %xeb = alloca i32, align 4 + %abcd = alloca [3 x i32], align 4 + %sy = alloca i32, align 4 + store double %0, double* %b, align 8 + %1 = load double, double* %b, align 8 + %2 = call double @llvm.ceil.f64(double %1) + store double %2, double* %d, align 8 + %3 = call double @llvm.maxnum.f64(double 1.000000e+00, double 1.000000e+00) + store double %3, double* %e, align 8 + %4 = load double, double* %d, align 8 + %5 = load double, double* %d, align 8 + %6 = load double, double* %d, align 8 + %7 = call double @llvm.fma.f64(double %4, double %5, double %6) + store double %7, double* %f, align 8 + store i32 13, i32* %xeb, align 4 + %8 = bitcast [3 x i32]* %abcd to i8* + call void @llvm.memset.p0i8.i64(i8* align 4 %8, i8 0, i64 12, i1 false) + %9 = load volatile i32, i32* %xeb, align 4 + store i32 %9, i32* %sy, align 4 + %10 = load i32, i32* %sy, align 4 + %add = add i32 %10, 1 + store volatile i32 %add, i32* %xeb, align 4 + %11 = getelementptr inbounds [3 x i32], [3 x i32]* %abcd, i64 0, i64 2 + %12 = load i32, i32* %sy, align 4 + %add1 = add i32 %12, 2 + store volatile i32 %add1, i32* %11, align 4 + %13 = getelementptr inbounds [3 x i32], [3 x i32]* %abcd, i64 0, i64 2 + %14 = load volatile i32, i32* %13, align 4 + store i32 %14, i32* %sy, align 4 + ret i32 1 +} \ No newline at end of file