Compare commits

..

6 Commits

Author SHA1 Message Date
Quentin Carbonneaux
7ab2fbe07c extern DYNCONST 2026-04-27 18:42:35 +02:00
Michael Forney
dba8d5a4bf update debug flag list for gvn/gcm 2026-04-27 10:08:45 +02:00
Michael Forney
504a2012f4 fix float neg on mach-o
neg for float uses xorp[sd] with a 16-byte memory operand.

This matches what clang emits with --target=x86_64-apple-darwin.
2026-04-25 18:51:46 +02:00
Quentin Carbonneaux
b58e2e695b fix exponential complexity in usewidthle() 2026-04-21 13:25:00 +02:00
willow
8ff0651552 parse: deny non-digit after minus in getint
also: '0' <= c <= '9' -> isdigit(c)
Signed-off-by: willow <im@purring.fyi>
2026-02-28 19:05:57 +01:00
willow
7ac9722ccb remove unused variable
Signed-off-by: willow <im@purring.fyi>
2026-02-28 19:05:15 +01:00
12 changed files with 156 additions and 28 deletions

11
all.h
View File

@ -292,9 +292,16 @@ struct Use {
}; };
struct Sym { struct Sym {
/* SGlo = 0 (direct access)
* SThr = 1 (local-exec TLS)
* SExt = 2 (GOT/PLT access)
* SExt|SThr = 3 (initial-exec TLS)
*/
enum { enum {
SGlo, SGlo = 0,
SThr, SThr = 1,
SExt = 2,
SExtThr = SExt|SThr,
} type; } type;
uint32_t id; uint32_t id;
}; };

View File

@ -195,7 +195,7 @@ emitcon(Con *con, E *e)
case CAddr: case CAddr:
l = str(con->sym.id); l = str(con->sym.id);
p = l[0] == '"' ? "" : T.assym; p = l[0] == '"' ? "" : T.assym;
if (con->sym.type == SThr) { if (con->sym.type & SThr) {
if (T.apple) if (T.apple)
fprintf(e->f, "%s%s@TLVP", p, l); fprintf(e->f, "%s%s@TLVP", p, l);
else else
@ -551,14 +551,48 @@ emitins(Ins i, E *e)
regtoa(i.to.val, SLong)); regtoa(i.to.val, SLong));
break; break;
} }
if (rtype(i.arg[0]) == RCon
&& e->fn->con[i.arg[0].val].sym.type == SExt) {
/* load address from the GOT */
assert(isreg(i.to));
con = &e->fn->con[i.arg[0].val];
sym = str(con->sym.id);
assert(!con->bits.i);
fprintf(e->f,
"\tmovq %s%s@GOTPCREL(%%rip), %%%s\n",
sym[0] == '"' ? "" : T.assym, sym,
regtoa(i.to.val, SLong));
break;
}
if (!T.apple
&& rtype(i.arg[0]) == RCon
&& e->fn->con[i.arg[0].val].sym.type == SExtThr) {
/* initial-exec TLS: load offset from
* GOT, add to thread-base register */
assert(isreg(i.to));
con = &e->fn->con[i.arg[0].val];
sym = str(con->sym.id);
assert(!con->bits.i);
emitf("movq %%fs:0, %L=", &i, e);
fprintf(e->f,
"\taddq %s%s@gottpoff(%%rip), %%%s\n",
sym[0] == '"' ? "" : T.assym, sym,
regtoa(i.to.val, SLong));
break;
}
goto Table; goto Table;
case Ocall: case Ocall:
/* calls simply have a weird syntax in AT&T /* calls simply have a weird syntax in AT&T
* assembly... */ * assembly... */
switch (rtype(i.arg[0])) { switch (rtype(i.arg[0])) {
case RCon: case RCon:
con = &e->fn->con[i.arg[0].val];
fprintf(e->f, "\tcallq "); fprintf(e->f, "\tcallq ");
emitcon(&e->fn->con[i.arg[0].val], e); emitcon(con, e);
if (con->type == CAddr
&& (con->sym.type & SExt)
&& !T.apple)
fprintf(e->f, "@PLT");
fprintf(e->f, "\n"); fprintf(e->f, "\n");
break; break;
case RTmp: case RTmp:

View File

@ -120,8 +120,34 @@ fixarg(Ref *r, int k, Ins *i, Fn *fn)
r1 = newtmp("isel", Kl, fn); r1 = newtmp("isel", Kl, fn);
emit(Oaddr, Kl, r1, SLOT(s), R); emit(Oaddr, Kl, r1, SLOT(s), R);
} }
else if (op != Ocall && hascon(r0, &c, fn)
&& c->type == CAddr && (c->sym.type & SExt)
&& (!T.apple || c->sym.type == SExt)) {
/* extern symbols need indirection
* through the GOT
*/
r1 = newtmp("isel", Kl, fn);
if (c->bits.i) {
r2 = newtmp("isel", Kl, fn);
cc = (Con){.type = CBits};
cc.bits.i = c->bits.i;
r3 = newcon(&cc, fn);
emit(Oadd, Kl, r1, r2, r3);
} else
r2 = r1;
cc = *c;
cc.bits.i = 0;
r3 = newcon(&cc, fn);
emit(Oaddr, Kl, r2, r3, R);
if (rtype(r0) == RMem) {
m = &fn->mem[r0.val];
m->offset.type = CUndef;
m->base = r1;
r1 = r0;
}
}
else if (T.apple && hascon(r0, &c, fn) else if (T.apple && hascon(r0, &c, fn)
&& c->type == CAddr && c->sym.type == SThr) { && c->type == CAddr && (c->sym.type & SThr)) {
r1 = newtmp("isel", Kl, fn); r1 = newtmp("isel", Kl, fn);
if (c->bits.i) { if (c->bits.i) {
r2 = newtmp("isel", Kl, fn); r2 = newtmp("isel", Kl, fn);

View File

@ -287,6 +287,17 @@ loadaddr(Con *c, char *rn, E *e)
"\tadd\tR, R, #:tprel_hi12:SO, lsl #12\n" "\tadd\tR, R, #:tprel_hi12:SO, lsl #12\n"
"\tadd\tR, R, #:tprel_lo12_nc:SO\n"; "\tadd\tR, R, #:tprel_lo12_nc:SO\n";
break; break;
case SExt:
if (T.apple)
s = "\tadrp\tR, S@gotpageO\n"
"\tldr\tR, [R, S@gotpageoffO]\n";
else
s = "\tadrp\tR, :got:SO\n"
"\tldr\tR, [R, #:got_lo12:SO]\n";
break;
case SExtThr:
die("extern thread not yet implemented on arm64");
break;
} }
l = str(c->sym.id); l = str(c->sym.id);
@ -468,7 +479,7 @@ emitins(Ins *i, E *e)
goto Table; goto Table;
c = &e->fn->con[i->arg[0].val]; c = &e->fn->con[i->arg[0].val];
if (c->type != CAddr if (c->type != CAddr
|| c->sym.type != SGlo || (c->sym.type & SThr)
|| c->bits.i) || c->bits.i)
die("invalid call argument"); die("invalid call argument");
l = str(c->sym.id); l = str(c->sym.id);

27
copy.c
View File

@ -41,9 +41,8 @@ bitwidth(uint64_t v)
return n+v; return n+v;
} }
/* no more than w bits are used */
static int static int
usewidthle(Fn *fn, Ref r, int w) uwl(Fn *fn, Ref r, int w)
{ {
Ext e; Ext e;
Tmp *t; Tmp *t;
@ -52,7 +51,6 @@ usewidthle(Fn *fn, Ref r, int w)
Ins *i; Ins *i;
Ref rc; Ref rc;
int64_t v; int64_t v;
int b;
assert(rtype(r) == RTmp); assert(rtype(r) == RTmp);
t = &fn->tmp[r.val]; t = &fn->tmp[r.val];
@ -69,21 +67,19 @@ usewidthle(Fn *fn, Ref r, int w)
if (p->visit || req(p->to, R)) if (p->visit || req(p->to, R))
continue; continue;
p->visit = 1; p->visit = 1;
b = usewidthle(fn, p->to, w); if (uwl(fn, p->to, w))
p->visit = 0;
if (b)
continue; continue;
break; break;
case UIns: case UIns:
i = u->u.ins; i = u->u.ins;
assert(i != 0); assert(i != 0);
if (i->op == Ocopy) if (i->op == Ocopy)
if (usewidthle(fn, i->to, w)) if (uwl(fn, i->to, w))
continue; continue;
if (ext(i, &e)) { if (ext(i, &e)) {
if (e.usew <= w) if (e.usew <= w)
continue; continue;
if (usewidthle(fn, i->to, w)) if (uwl(fn, i->to, w))
continue; continue;
} }
if (i->op == Oand) { if (i->op == Oand) {
@ -107,6 +103,21 @@ usewidthle(Fn *fn, Ref r, int w)
return 1; return 1;
} }
/* no more than w bits are used */
static int
usewidthle(Fn *fn, Ref r, int w)
{
Blk *b;
Phi *p;
int ret;
ret = uwl(fn, r, w);
for (b=fn->start; b; b=b->link)
for (p=b->phi; p; p=p->link)
p->visit = 0;
return ret;
}
static int static int
min(int v1, int v2) min(int v1, int v2)
{ {

View File

@ -180,7 +180,9 @@ by zero-extension, or by sign-extension.
DYNCONST := DYNCONST :=
CONST CONST
| 'thread' $IDENT # Thread-local symbol | 'thread' $IDENT # Thread-local symbol
| 'extern' $IDENT # Extern symbol (GOT)
| 'extern' 'thread' $IDENT # Extern thread-local (initial-exec)
VAL := VAL :=
DYNCONST DYNCONST
@ -225,6 +227,16 @@ When the `thread` keyword prefixes a symbol name, the
symbol's numeric value is resolved at runtime in the symbol's numeric value is resolved at runtime in the
thread-local storage. thread-local storage.
When the `extern` keyword prefixes a symbol name, the
symbol is accessed indirectly through the Global Offset
Table (GOT). Function calls to extern symbols use the
Procedure Linkage Table (PLT). This enables PIE and
PIC code generation for symbols that are not part of
the main executable. When `extern` is combined with
`thread`, the symbol is accessed using the initial-exec
TLS model, suitable for thread-local variables defined
in shared libraries.
Vals are used as arguments in regular, phi, and jump Vals are used as arguments in regular, phi, and jump
instructions within function definitions. They are instructions within function definitions. They are
either constants or function-scope temporaries. either constants or function-scope temporaries.

2
emit.c
View File

@ -221,7 +221,7 @@ macho_emitfin(FILE *f)
static char *sec[3] = { static char *sec[3] = {
"__TEXT,__literal4,4byte_literals", "__TEXT,__literal4,4byte_literals",
"__TEXT,__literal8,8byte_literals", "__TEXT,__literal8,8byte_literals",
".abort \"unreachable\"", "__TEXT,__literal16,16byte_literals",
}; };
emitfin(f, sec); emitfin(f, sec);

4
gvn.c
View File

@ -43,11 +43,11 @@ static uint gvntbln;
static Ins * static Ins *
gvndup(Ins *i, int insert) gvndup(Ins *i, int insert)
{ {
uint idx, n; uint idx;
Ins *ii; Ins *ii;
idx = ihash(i) % gvntbln; idx = ihash(i) % gvntbln;
for (n=1;; n++) { for (;;) {
ii = gvntbl[idx]; ii = gvntbl[idx];
if (!ii) if (!ii)
break; break;

2
main.c
View File

@ -10,7 +10,7 @@ char debug['Z'+1] = {
['M'] = 0, /* memory optimization */ ['M'] = 0, /* memory optimization */
['N'] = 0, /* ssa construction */ ['N'] = 0, /* ssa construction */
['C'] = 0, /* copy elimination */ ['C'] = 0, /* copy elimination */
['F'] = 0, /* constant folding */ ['G'] = 0, /* gvn/gcm */
['K'] = 0, /* if-conversion */ ['K'] = 0, /* if-conversion */
['A'] = 0, /* abi lowering */ ['A'] = 0, /* abi lowering */
['I'] = 0, /* instruction selection */ ['I'] = 0, /* instruction selection */

22
parse.c
View File

@ -57,6 +57,7 @@ enum Token {
Thlt, Thlt,
Texport, Texport,
Tthread, Tthread,
Textern,
Tcommon, Tcommon,
Tfunc, Tfunc,
Ttype, Ttype,
@ -116,6 +117,7 @@ static char *kwmap[Ntok] = {
[Thlt] = "hlt", [Thlt] = "hlt",
[Texport] = "export", [Texport] = "export",
[Tthread] = "thread", [Tthread] = "thread",
[Textern] = "extern",
[Tcommon] = "common", [Tcommon] = "common",
[Tfunc] = "function", [Tfunc] = "function",
[Ttype] = "type", [Ttype] = "type",
@ -215,12 +217,15 @@ getint()
n = 0; n = 0;
c = fgetc(inf); c = fgetc(inf);
m = (c == '-'); m = (c == '-');
if (m) if (m) {
c = fgetc(inf); c = fgetc(inf);
if (!isdigit(c))
err("integer expected");
}
do { do {
n = 10*n + (c - '0'); n = 10*n + (c - '0');
c = fgetc(inf); c = fgetc(inf);
} while ('0' <= c && c <= '9'); } while (isdigit(c));
ungetc(c, inf); ungetc(c, inf);
if (m) if (m)
n = 1 + ~n; n = 1 + ~n;
@ -445,11 +450,20 @@ parseref()
c.bits.d = tokval.fltd; c.bits.d = tokval.fltd;
c.flt = 2; c.flt = 2;
break; break;
case Textern:
c.sym.type = SExt;
if (peek() == Tthread) {
next();
c.sym.type |= SThr;
}
expect(Tglo);
goto Glo;
case Tthread: case Tthread:
c.sym.type = SThr; c.sym.type = SThr;
expect(Tglo); expect(Tglo);
/* fall through */ /* fall through */
case Tglo: case Tglo:
Glo:
c.type = CAddr; c.type = CAddr;
c.sym.id = intern(tokval.str); c.sym.id = intern(tokval.str);
break; break;
@ -1252,7 +1266,9 @@ printcon(Con *c, FILE *f)
case CUndef: case CUndef:
break; break;
case CAddr: case CAddr:
if (c->sym.type == SThr) if (c->sym.type & SExt)
fprintf(f, "extern ");
if (c->sym.type & SThr)
fprintf(f, "thread "); fprintf(f, "thread ");
fprintf(f, "$%s", str(c->sym.id)); fprintf(f, "$%s", str(c->sym.id));
if (c->bits.i) if (c->bits.i)

View File

@ -131,7 +131,7 @@ slot(Ref r, Fn *fn)
static void static void
emitaddr(Con *c, FILE *f) emitaddr(Con *c, FILE *f)
{ {
assert(c->sym.type == SGlo); assert(c->sym.type == SGlo || c->sym.type == SExt);
fputs(str(c->sym.id), f); fputs(str(c->sym.id), f);
if (c->bits.i) if (c->bits.i)
fprintf(f, "+%"PRIi64, c->bits.i); fprintf(f, "+%"PRIi64, c->bits.i);
@ -231,7 +231,8 @@ loadaddr(Con *c, char *rn, FILE *f)
{ {
char off[32]; char off[32];
if (c->sym.type == SThr) { switch (c->sym.type) {
case SThr:
if (c->bits.i) if (c->bits.i)
sprintf(off, "+%"PRIi64, c->bits.i); sprintf(off, "+%"PRIi64, c->bits.i);
else else
@ -242,10 +243,20 @@ loadaddr(Con *c, char *rn, FILE *f)
rn, rn, str(c->sym.id), off); rn, rn, str(c->sym.id), off);
fprintf(f, "\taddi %s, %s, %%tprel_lo(%s)%s\n", fprintf(f, "\taddi %s, %s, %%tprel_lo(%s)%s\n",
rn, rn, str(c->sym.id), off); rn, rn, str(c->sym.id), off);
} else { break;
case SExt:
fprintf(f, "\tla %s, ", rn); fprintf(f, "\tla %s, ", rn);
emitaddr(c, f); emitaddr(c, f);
fputc('\n', f); fputc('\n', f);
break;
case SExtThr:
die("extern thread not yet implemented on rv64");
break;
default:
fprintf(f, "\tla %s, ", rn);
emitaddr(c, f);
fputc('\n', f);
break;
} }
} }
@ -282,7 +293,7 @@ fixmem(Ref *pr, Fn *fn, FILE *f)
if (rtype(r) == RCon) { if (rtype(r) == RCon) {
c = &fn->con[r.val]; c = &fn->con[r.val];
if (c->type == CAddr) if (c->type == CAddr)
if (c->sym.type == SThr) { if (c->sym.type != SGlo) {
loadcon(c, T6, Kl, f); loadcon(c, T6, Kl, f);
*pr = TMP(T6); *pr = TMP(T6);
} }
@ -387,7 +398,7 @@ emitins(Ins *i, Fn *fn, FILE *f)
case RCon: case RCon:
con = &fn->con[i->arg[0].val]; con = &fn->con[i->arg[0].val];
if (con->type != CAddr if (con->type != CAddr
|| con->sym.type != SGlo || (con->sym.type & SThr)
|| con->bits.i) || con->bits.i)
goto Invalid; goto Invalid;
fprintf(f, "\tcall %s\n", str(con->sym.id)); fprintf(f, "\tcall %s\n", str(con->sym.id));

View File

@ -29,7 +29,7 @@ char *tok[] = {
"function", "type", "data", "section", "align", "dbgfile", "function", "type", "data", "section", "align", "dbgfile",
"blit", "l", "w", "sh", "uh", "h", "sb", "ub", "b", "blit", "l", "w", "sh", "uh", "h", "sb", "ub", "b",
"d", "s", "z", "loadw", "loadl", "loads", "loadd", "d", "s", "z", "loadw", "loadl", "loads", "loadd",
"alloc1", "alloc2", "thread", "common", "alloc1", "alloc2", "thread", "extern", "common",
}; };
enum { enum {