aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThomas Voss <mail@thomasvoss.com> 2024-06-24 09:05:56 +0200
committerThomas Voss <mail@thomasvoss.com> 2024-06-24 09:05:56 +0200
commit8810e260f591a780feb251fa5dcaa195b2dcc366 (patch)
tree8893309ee1d041614cbbce2a66a136c804797a0b
parent398081f8960d34b1a9c28f9d2736f15a80d02c85 (diff)
Try to handle floating point numbers better
-rw-r--r--make.c2
-rw-r--r--src/analyzer.c21
-rw-r--r--src/codegen.c31
3 files changed, 44 insertions, 10 deletions
diff --git a/make.c b/make.c
index c57362d..f96a039 100644
--- a/make.c
+++ b/make.c
@@ -300,7 +300,7 @@ ld(void)
if (!rflag && !Sflag)
strspushl(&cmd, "-fsanitize=address,undefined");
llvmquery(&cmd, LLVM_LDFLAGS | LLVM_LIBS);
- strspushl(&cmd, "-o", TARGET);
+ strspushl(&cmd, "-lm", "-o", TARGET);
assert(glob("src/*.o", 0, globerr, &g) == 0);
for (size_t i = 0; i < g.gl_pathc; i++) {
diff --git a/src/analyzer.c b/src/analyzer.c
index a449653..c7cf3a5 100644
--- a/src/analyzer.c
+++ b/src/analyzer.c
@@ -1,5 +1,7 @@
#include <assert.h>
#include <ctype.h>
+#include <limits.h>
+#include <math.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
@@ -17,6 +19,10 @@
#include "symtab.h"
#include "types.h"
+#define LOG2_10 (3.321928)
+#define MP_BITCNT_MAX ((mp_bitcnt_t)-1)
+#define MIN(x, y) ((x) < (y) ? (x) : (y))
+
/* In debug builds we want to actually alloc a new mpq_t so that it’s
easier to free memory without doing a double free */
#if DEBUG
@@ -259,11 +265,14 @@ analyzeexpr(struct azctx ctx, scope_t *scps, type_t *types, ast_t ast,
aux_t aux, lexemes_t toks, idx_t i)
{
switch (ast.kinds[i]) {
- case ASTNUMLIT:
+ case ASTNUMLIT: {
+ strview_t sv = toks.strs[ast.lexemes[i]];
types[i].kind = TYPE_NUM;
types[i].size = 0;
types[i].issigned = true;
+ types[i].isfloat = memchr(sv.p, '.', sv.len) != NULL;
return fwdnode(ast, i);
+ }
case ASTIDENT: {
strview_t sv = toks.strs[ast.lexemes[i]];
@@ -403,11 +412,13 @@ constfoldexpr(struct cfctx ctx, mpq_t *folds, scope_t *scps, type_t *types,
buf[len] = 0;
if (isfloat) {
+ /* TODO: Is this correct? It seems to work… but I don’t know
+ if there is a better way to do this; sometimes the
+ precision is a few bits more than it could be. */
mpf_t x;
- /* FIXME: This uses global precision, not thread safe! */
- /* FIXME: This doesn’t try to figure out what the correct
- precision is. */
- int ret = mpf_init_set_str(x, buf, 10);
+ double prec = ceil((sv.len - 1) * LOG2_10);
+ mpf_init2(x, MIN(MP_BITCNT_MAX, (mp_bitcnt_t)prec));
+ int ret = mpf_set_str(x, buf, 10);
assert(ret == 0);
mpq_set_f(folds[i], x);
mpf_clear(x);
diff --git a/src/codegen.c b/src/codegen.c
index 69f0214..d036b74 100644
--- a/src/codegen.c
+++ b/src/codegen.c
@@ -155,13 +155,36 @@ codegentypedexpr(struct cgctx ctx, idx_t i, type_t type, LLVMValueRef *outv)
*outv = LLVMConstIntOfString(type2llvm(ctx, type), buf, 10);
return fwdnode(ctx.ast, i);
} else if (MPQ_IS_INIT(ctx.folds[i]) /* && type.isfloat */) {
- /* FIXME: This is bad and broken */
+ char *s, *buf;
+ size_t len;
mpf_t x;
- mpf_init(x);
+ mp_exp_t e;
+ mp_bitcnt_t prec;
+
+ /* TODO: Is this even correct? */
+ switch (type.size) {
+ case 2: prec = 5; break;
+ case 4: prec = 8; break;
+ case 8: prec = 11; break;
+ case 16: prec = 16; break;
+ default: __builtin_unreachable();
+ }
+
+ mpf_init2(x, prec);
mpf_set_q(x, ctx.folds[i]);
- char buf[256];
- gmp_snprintf(buf, sizeof(buf), "%Ff", x);
+
+ s = mpf_get_str(NULL, &e, 10, 0, x);
+ len = strlen(s);
+ buf = tmpalloc(ctx.s, len + 2, 1);
+ for (size_t i = 0; i < (size_t)e; i++)
+ buf[i] = s[i];
+ buf[e] = '.';
+ for (size_t i = e; i < len; i++)
+ buf[i + 1] = s[i];
+ buf[len + 1] = 0;
*outv = LLVMConstRealOfString(type2llvm(ctx, type), buf);
+
+ free(s);
mpf_clear(x);
return fwdnode(ctx.ast, i);
}