aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorlemon <lsof@mailbox.org>2022-08-26 22:08:52 +0200
committerlemon <lsof@mailbox.org>2022-08-26 22:11:30 +0200
commit4a7d610bb9a441f6fb7f88caaa803c1c85e4fcfd (patch)
tree3ac01c9be375f30efd09f0a670da74f886ad44ca
parent475125eaba2852f88a4ee8d9be64c8b4964df3a9 (diff)
bitfields
-rw-r--r--misc/cff.vim2
-rw-r--r--src/cffc.hff18
-rw-r--r--src/fmt.cff6
-rw-r--r--src/llvm.cff92
-rw-r--r--src/parse.cff172
-rw-r--r--src/type.cff6
-rw-r--r--test/2.cff17
-rw-r--r--x.ll309
8 files changed, 260 insertions, 362 deletions
diff --git a/misc/cff.vim b/misc/cff.vim
index 0d64b5b..8bf358e 100644
--- a/misc/cff.vim
+++ b/misc/cff.vim
@@ -15,7 +15,7 @@ syn keyword Type i8 u8 i16 u16 i32 u32 i64 u64
syn keyword Type int uint isize usize iptrint uptrint
syn keyword Type c_char c_long c_ulong c_llong c_ullong
syn keyword Type bool intbool void f32 f64 typeof
-syn keyword Structure struct enum union
+syn keyword Structure struct enum union bitfield
syn keyword Operator alignof sizeof as and or offsetof
syn match PreProc "#\i*\>"
diff --git a/src/cffc.hff b/src/cffc.hff
index d1d423f..066c1f7 100644
--- a/src/cffc.hff
+++ b/src/cffc.hff
@@ -19,8 +19,8 @@ struct Loc {
#[lax]
enum TokT : i32 {
// !sorted
- kw_alignof, kw_and, kw_as, kw_break, kw_case, kw_const,
- kw_continue, kw_def, kw_defer, kw_defmacro, kw_do,
+ kw_alignof, kw_and, kw_as, kw_bitfield, kw_break, kw_case,
+ kw_const, kw_continue, kw_def, kw_defer, kw_defmacro, kw_do,
kw_else, kw_enum, kw_extern, kw_fn,
kw_for, kw_if, kw_import, kw_let, kw_offsetof,
kw_or, kw_return, kw_sizeof, kw_static,
@@ -71,6 +71,12 @@ struct AggField {
struct EnumVal { name *const u8, i i64 }
+struct BitFField {
+ name *const u8,
+ ty *const Type,
+ off u16, size u16
+}
+
enum union TeplArg;
struct Type {
@@ -108,6 +114,12 @@ struct Type {
flds [#]AggField,
decls *Env,
},
+ BitF struct {
+ intty *const Type,
+ name *const u8,
+ id int,
+ flds [#]BitFField,
+ },
VaList,
},
@@ -178,9 +190,11 @@ struct Expr {
Cond struct { test *Expr, t *Expr, f *Expr },
Cast *Expr,
Dot struct { lhs *Expr, fld *const AggField },
+ BitDot struct { lhs *Expr, fld *const BitFField },
SLen *Expr,
SPtr *Expr,
EUTag *Expr,
+ BitRaw *Expr,
Index struct { lhs *Expr, rhs *Expr },
Slice struct { lhs *Expr, begin *Expr, end *Expr },
Call struct { lhs *Expr, args [#]Expr },
diff --git a/src/fmt.cff b/src/fmt.cff
index 8c0f6a4..6707222 100644
--- a/src/fmt.cff
+++ b/src/fmt.cff
@@ -198,6 +198,12 @@ extern fn vpfmt(proc *fn(u8, *void) void, parg *void, fmt *const u8, ap va_list)
} else {
ps("(anonymous)");
}
+ case BitF b;
+ if b.name {
+ ps(b.name);
+ } else {
+ ps("(anonymous)");
+ }
}
}
diff --git a/src/llvm.cff b/src/llvm.cff
index 8ff32ba..32b1066 100644
--- a/src/llvm.cff
+++ b/src/llvm.cff
@@ -96,6 +96,7 @@ fn gen(fmt *const u8, ...) void {
case Agg;
genagg(ty);
case Enum enu; pritype(enu.intty);
+ case BitF bitf; pritype(bitf.intty);
case Fn f;
gen("%t(", f.ret);
foreach(ty, i, f.params) {
@@ -202,6 +203,16 @@ fn mktmp(ty *const Type) Value {
return Value{ty, :Tmp(tmpid++)};
}
+defmacro gentmp(type, ...rest) [
+ (do
+ let $tmp = mktmp(type);
+ gen("\t%v = ", $tmp);
+ gen(rest);
+ gen("\n");
+ $tmp;
+ )
+]
+
fn convert(f *Fn, to *const Type, ex *Expr) Value;
fn genexpr(f *Fn, ex *Expr) Value;
static arena Arena = {};
@@ -235,20 +246,18 @@ fn genaddr(f *Fn, ex *Expr) Value {
switch idx.lhs.ty.u {
case Arr arrty;
let arr = genaddr(f, idx.lhs);
- lhs = mktmp(mkptrtype(ex.ty));
- gen("\t%v = getelementptr %t, %t %v, i32 0, i32 0\n", lhs, idx.lhs.ty, arr.ty, arr);
+ lhs = gentmp(mkptrtype(ex.ty), "getelementptr %t, %t %v, i32 0, i32 0", idx.lhs.ty, arr.ty, arr);
case Ptr;
lhs = genexpr(f, idx.lhs);
case Slice child;
let tmp = genexpr(f, idx.lhs);
- lhs = mktmp(mkptrtype(child));
- gen("\t%v = extractvalue %t %v, 0\n", lhs, tmp.ty, tmp);
+ lhs = gentmp(mkptrtype(child), "extractvalue %t %v, 0", tmp.ty, tmp);
case else;
assert(#f, "index");
}
- let rhs = genexpr(f, idx.rhs),
- addr = mktmp(mkptrtype(ex.ty));
- gen("\t%v = getelementptr %t, %t %v, %t %v\n", addr, ex.ty, lhs.ty, lhs, rhs.ty, rhs);
+ let rhs = genexpr(f, idx.rhs);
+ let addr = gentmp(mkptrtype(ex.ty), "getelementptr %t, %t %v, %t %v",
+ ex.ty, lhs.ty, lhs, rhs.ty, rhs);
return addr;
case Dot dot;
@@ -262,27 +271,30 @@ fn genaddr(f *Fn, ex *Expr) Value {
} else {
idx = dot.fld - dot.lhs.ty.u.Agg.flds.#ptr;
}
- let addr = mktmp(mkptrtype(ex.ty));
+ let addr Value #?;
+ let ptrty = mkptrtype(ex.ty);
switch lhs.ty.u.Ptr.u.Agg.kind {
case :Struct;
- gen("\t%v = getelementptr %t, %t %v, i32 0, i32 %d\n", addr, lhs.ty.u.Ptr, lhs.ty, lhs, idx);
+ addr = gentmp(ptrty, "getelementptr %t, %t %v, i32 0, i32 %d",
+ lhs.ty.u.Ptr, lhs.ty, lhs, idx);
case :Union;
- gen("\t%v = bitcast %t %v to %t", addr, lhs.ty, lhs, addr.ty);
+ addr = gentmp(ptrty, "bitcast %t %v to %t", lhs.ty, lhs, ptrty);
case else
let off int = dot.fld.off;
- gen("\t%v = getelementptr i8, %t %v, i32 %d\n", addr, lhs.ty, lhs, off);
+ addr = gentmp(ptrty, "getelementptr i8, %t %v, i32 %d", lhs.ty, lhs, off);
}
return addr;
case EUTag eex;
let lhs = eex.ty->is(:Ptr) ? genexpr(f, eex) : genaddr(f, eex);
- let addr = mktmp(mkptrtype(ex.ty));
- gen("\t%v = bitcast %t %v to %t", addr, lhs.ty, lhs, addr.ty);
+ let addr = gentmp(mkptrtype(ex.ty), "bitcast %t %v to %t", lhs.ty, lhs, mkptrtype(ex.ty));
return addr;
+ case BitRaw ex;
+ return genaddr(f, ex);
+
case AggIni ini;
- let tmp = mktmp(mkptrtype(ex.ty));
- gen("\t%v = alloca %t\n", tmp, ex.ty);
+ let tmp = gentmp(mkptrtype(ex.ty), "alloca %t", ex.ty);
gen("\tstore %t zeroinitializer, %t %v\n", ex.ty, tmp.ty, tmp);
foreach(fld, i, ini.flds) {
let fex = &ini.exs[i];
@@ -300,8 +312,7 @@ fn genaddr(f *Fn, ex *Expr) Value {
case ArrIni ini;
let ty = mkarrtype(ini.maxn, #f, ex.ty.u.Arr.child);
- let tmp = mktmp(mkptrtype(ty));
- gen("\t%v = alloca %t\n", tmp, ty);
+ let tmp = gentmp(mkptrtype(ty), "alloca %t", ty);
gen("\tstore %t zeroinitializer, %t %v\n", ty, tmp.ty, tmp);
foreach(idx, i, ini.idxs) {
let fex = &ini.exs[i];
@@ -524,10 +535,28 @@ fn genexpr(f *Fn, ex *Expr) Value {
case '>'; gencmp(ty2->is(:Flo) ? "ogt" : ty2.u.Int.sgn ? "sgt" : "ugt");
case '>='; gencmp(ty2->is(:Flo) ? "oge" : ty2.u.Int.sgn ? "sge" : "uge");
case '=';
- let addr = genaddr(f, b.lhs);
- let rhs = convert(f, b.lhs.ty, b.rhs);
- gen("\tstore %t %v, %t %v\n", ex.ty, rhs, addr.ty, addr);
- return rhs;
+ if b.lhs.u.#tag != :BitDot {
+ let addr = genaddr(f, b.lhs);
+ let rhs = convert(f, b.lhs.ty, b.rhs);
+ gen("\tstore %t %v, %t %v\n", ex.ty, rhs, addr.ty, addr);
+ return rhs;
+ } else {
+ let dot = &b.lhs.u.BitDot;
+ let intty = dot.lhs.ty.u.BitF.intty;
+ let fld = dot.fld;
+ let srcmask = fld.size == 64 ? ~0u64 : ((1u64 << fld.size) - 1);
+ let addr = genaddr(f, dot.lhs);
+ let src0 = convert(f, intty, b.rhs);
+ let src = gentmp(intty, "and %t %v, %I", src0.ty, src0, srcmask);
+ let src = gentmp(intty, "shl %t %v, %d", src.ty, src, fld.off);
+ let dstmask = ~(srcmask << fld.off);
+ let lhs = gentmp(intty, "load %t, %t %v", intty, addr.ty, addr);
+ let lhs = gentmp(lhs.ty, "and %t %v, %I", lhs.ty, lhs, dstmask);
+ let raw = gentmp(lhs.ty, "or %t %v, %v", lhs.ty, lhs, src);
+ gen("\tstore %t %v, %t %v\n", raw.ty, raw, addr.ty, addr);
+ let res = gentmp(intty, "load %t, %t %v", fld.ty, addr.ty, addr);
+ return res;
+ }
case '+=';
let addr = genaddr(f, b.lhs);
let rhs = convert(f, b.lhs.ty, b.rhs);
@@ -696,6 +725,27 @@ fn genexpr(f *Fn, ex *Expr) Value {
gen("\t%v = load %t, %t %v\n", tmp, ex.ty, addr.ty, addr);
return tmp;
+ case BitRaw ex;
+ return genexpr(f, ex);
+
+ case BitDot dot;
+ let fld = dot.fld;
+ let lhs = genexpr(f, dot.lhs);
+ let shifted = fld.off == 0 ? lhs : gentmp(lhs.ty, "lshr %t %v, %d", lhs.ty, lhs, fld.off);
+ let truncd = gentmp(#null, "trunc %t %v to i%d", shifted.ty, shifted, fld.size);
+ let res = mktmp(fld.ty);
+ switch {
+ case dot.fld.ty->is(:Bool);
+ let tmp = gentmp(ty_bool, "icmp ne i%d %v, 0", fld.size, truncd);
+ gen("\t%v = zext i1 %v to %t\n", res, tmp, res.ty);
+
+ case dot.fld.ty->is(:Int);
+ gen("\t%v = %s i%d %v to %t\n", res, dot.fld.ty.u.Int.sgn ? "sext" : "zext", truncd, fld.size, lhs.ty);
+
+ case else assert(#f, "");
+ }
+ return res;
+
case SPtr sl;
let sl = genexpr(f, sl);
let ptr = mktmp(mkptrtype(sl.ty.u.Slice));
diff --git a/src/parse.cff b/src/parse.cff
index d4b8b7a..364b23b 100644
--- a/src/parse.cff
+++ b/src/parse.cff
@@ -141,8 +141,8 @@ fn eatspaces(P *Parser) void {
// !sorted
extern static keyword2str [NUM_KEYWORDS]*const u8 = {
- "alignof", "and", "as", "break", "case", "const",
- "continue", "def", "defer", "defmacro", "do",
+ "alignof", "and", "as", "bitfield", "break", "case",
+ "const", "continue", "def", "defer", "defmacro", "do",
"else", "enum", "extern", "fn",
"for", "if", "import", "let", "offsetof",
"or", "return", "sizeof", "static",
@@ -400,6 +400,8 @@ fn lex(P *Parser) Tok {
tok.t = '#ptr';
case streq(s, "#tag");
tok.t = '#tag';
+ case streq(s, "#raw");
+ tok.t = '#raw';
case streq(s, "#FILE");
tok.t = '#FIL';
case streq(s, "#LINE");
@@ -693,7 +695,7 @@ fn parseenum(P *Parser, name *const u8, lax bool) *const Type {
fn isdecltokt(t TokT) bool {
switch t {
case :kw_fn, :kw_static, :kw_def, :kw_defmacro, :kw_struct, :kw_union, :kw_enum,
- :kw_extern;
+ :kw_extern, :kw_bitfield;
return #t;
}
return #f;
@@ -824,6 +826,79 @@ fn findaggfield(ty *const Type, name *const u8) *AggField {
return #null;
}
+fn parsebitfield(P *Parser, name *const u8) *const Type {
+ let ty Type = { .u: :BitF{} };
+ let bitf = &ty.u.BitF;
+ let tok = lexexpect(P, ':');
+ bitf.name = name;
+ bitf.intty = parsetype(P);
+ if !bitf.intty->is(:Int) or bitf.intty.u.Int.sgn {
+ fatal(P, tok.loc, "bitfield backing type must be unsigned integer (got %t)", bitf.intty);
+ }
+ ty.size = bitf.intty.size;
+ ty.align = bitf.intty.align;
+ lexexpect(P, '{');
+ static id int = {};
+ bitf.id = id++;
+ let flds Vec<BitFField> = {};
+ let iota = 0u16;
+ while !lexmatch(P, &tok, '}') {
+ let name = (tok = lexexpects(P, :ident, "field name")).u.ident;
+ let off u16 = 0, size u16 = 0;
+ let ty = bitf.intty;
+
+ if lexmatch(P, &tok, '(') {
+ // `(offset, size)`
+ let off_ex = parseexpr(P);
+ lexexpect(P, ',');
+ let size_ex = parseexpr(P);
+ lexmatch(P, &tok, ',');
+ lexexpect(P, ')');
+ if !fold(&off_ex) or off_ex.u.#tag != :IntLit or off_ex.u.IntLit.i < 0 {
+ fatal(P, off_ex.loc, "expected non-negative constant integer for field offset");
+ }
+ if !fold(&size_ex) or size_ex.u.#tag != :IntLit or size_ex.u.IntLit.i <= 0 {
+ fatal(P, size_ex.loc, "expected positive constant integer for field size");
+ }
+ off = off_ex.u.IntLit.i;
+ size = size_ex.u.IntLit.i;
+ } else {
+ // `size` (offset is right after previous field)
+ let size_ex = parseexpr(P);
+ if !fold(&size_ex) or size_ex.u.#tag != :IntLit or size_ex.u.IntLit.i <= 0 {
+ fatal(P, size_ex.loc, "expected positive constant integer for field size");
+ }
+ off = iota;
+ size = size_ex.u.IntLit.i;
+ }
+
+ if (tok = lexpeek(P)).t == :ident and streq(tok.u.ident, "bool") {
+ lex(P);
+ ty = ty_bool;
+ } else if (tok = lexpeek(P)).t == :ident and streq(tok.u.ident, "signed") {
+ lex(P);
+ let signedty = *bitf.intty;
+ signedty.u.Int.sgn = #t;
+ ty = interntype(signedty);
+ }
+
+ iota += size;
+ if off + size > bitf.intty.size * 8 {
+ err(P, tok.loc, "field `%s' outside %t bit range", name, bitf.intty);
+ }
+
+ flds->push({ name, ty, off, size });
+
+ if !lexmatch(P, #null, ',') {
+ lexexpect(P, '}');
+ break;
+ }
+ }
+
+ bitf.flds = flds->move(P.alloc);
+ return interntype(ty);
+}
+
fn typematchestarg(targ *const Type, ty *const Type) bool {
if targ == ty {
return #t;
@@ -1077,9 +1152,12 @@ fn parsetype(P *Parser) *const Type {
if lexmatch(P, #null, :kw_union) {
return parseagg(P, tok.loc, :EUnion, #null, &decl);
} else {
- return parseenum(P, #null, #{lax?} #f);
+ return parseenum(P, #{name} #null, #{lax?} #f);
}
+ case lexmatch(P, &tok, :kw_bitfield);
+ return parsebitfield(P, #{name} #null);
+
case else;
fatal(P, tok.loc, "expected type (near %qT)", tok);
}
@@ -1093,7 +1171,7 @@ fn exprdup(alloc *Allocator, ex Expr) *Expr {
return memcpy(alloc->alloc(sizeof(ex), alignof(ex)), &ex, sizeof(ex));
}
-fn islvalue(ex Expr) bool {
+fn islvalue(ex *Expr) bool {
switch ex.u {
case Symbol decl;
return decl.u.#tag == :Let or decl.u.#tag == :Static;
@@ -1103,6 +1181,10 @@ fn islvalue(ex Expr) bool {
return #t;
case Dot;
return #t;
+ case BitDot bdot;
+ return islvalue(bdot.lhs);
+ case BitRaw ex;
+ return islvalue(ex);
case EUTag;
return #t;
}
@@ -1588,12 +1670,16 @@ fn pexpostfix(P *Parser) Expr {
if !isnumtype(ex.ty) and !ex.ty->is(:Ptr) {
fatal(P, ex.loc, "invalid operand to unary operator %qT (%t)", tok, ex.ty);
}
- if !islvalue(ex) {
+ if !islvalue(&ex) {
err(P, ex.loc, "left operand to prefix %qT operator is not lvalue", tok);
}
if ex.ty.konst {
err(P, ex.loc, "left operand to prefix %qT operator is const", tok);
}
+ if ex.u.#tag == :BitDot {
+ err(P, ex.loc, "cannot apply post-%srement operator to bitfield value",
+ tok.t == '++' ? "inc" : "dec");
+ }
ex = {
tok.loc, ex.ty, .u: :UnOp {
tok.t == '++' ? :postinc : :postdec, exprdup(P.alloc, ex)
@@ -1607,26 +1693,32 @@ fn pexpostfix(P *Parser) Expr {
if ty->is(:Ptr) {
ty = ty.u.Ptr;
}
- if !ty->is(:Agg) {
- fatal(P, tok.loc, "left-hand-side is not an aggregate (%t)", ty);
- }
let konst = ty.konst;
- ty = unconstify(ty);
- let agg = &ty.u.Agg;
- let fld *AggField = #null;
- foreach(f, i, agg.flds) {
- if streq(name, f.name) {
- fld = &agg.flds[i];
- break;
+ switch ty.u {
+ case Agg *agg;
+ let fld *AggField = #null;
+ foreach(f, i, agg.flds) {
+ if streq(name, f.name) {
+ fld = &agg.flds[i];
+ break;
+ }
}
+ if fld == #null { fatal(P, tok.loc, "%t has no such field %qT", ty, tok); }
+ if fld.ty == #null { fatal(P, tok.loc, "field %qT has no type", tok); }
+ ex = { tok.loc, konst ? constify(fld.ty) : fld.ty, :Dot { exprdup(P.alloc, ex), fld }};
+ case BitF *bitf;
+ let fld *BitFField = #null;
+ foreach(f, i, bitf.flds) {
+ if streq(name, f.name) {
+ fld = &bitf.flds[i];
+ break;
+ }
+ }
+ if fld == #null { fatal(P, tok.loc, "%t has no such field %qT", ty, tok); }
+ ex = { tok.loc, konst ? constify(fld.ty) : fld.ty, :BitDot { exprdup(P.alloc, ex), fld }};
+ case else
+ fatal(P, tok.loc, "left-hand-side is not an aggregate (%t)", ty);
}
- if fld == #null {
- fatal(P, tok.loc, "%t has no such field %qT", ty, tok);
- }
- if fld.ty == #null {
- fatal(P, tok.loc, "field %qT has no type", tok);
- }
- ex = { tok.loc, konst ? constify(fld.ty) : fld.ty, :Dot { exprdup(P.alloc, ex), fld }};
case lexmatch(P, &tok, '#ptr');
let ty = ex.ty;
@@ -1658,6 +1750,17 @@ fn pexpostfix(P *Parser) Expr {
let enumty = ty.u.Agg.enumty;
ex = { tok.loc, ty.konst ? constify(enumty) : enumty, :EUTag(exprdup(P.alloc, ex)) };
+ case lexmatch(P, &tok, '#raw');
+ let ty = ex.ty;
+ if ty->is(:Ptr) {
+ ty = ty.u.Ptr;
+ }
+ if !ty->is(:BitF) {
+ fatal(P, ex.loc, "invalid operand to #raw (%t)", ex.ty);
+ }
+ let intty = ty.u.BitF.intty;
+ ex = { tok.loc, ty.konst ? constify(intty) : intty, :BitRaw(exprdup(P.alloc, ex)) };
+
case lexpeek(P).t == '[';
// sugar: expr.[idx] -> (*expr)[idx]
if !ex.ty->is(:Ptr) {
@@ -1709,7 +1812,7 @@ fn pexpostfix(P *Parser) Expr {
ex = { ex.loc, ty, :UnOp{:deref, exprdup(P.alloc, ex)}};
case !exptr and metptr;
- if !islvalue(ex) {
+ if !islvalue(&ex) {
err(P, tok.loc, "cannot call `->%s' by reference, lhs is not an lvalue", name);
} else if ty.konst and !recv.konst {
err(P, tok.loc, "constness mismatch: method takes %t but got %t", recv0, ex.ty);
@@ -1773,12 +1876,16 @@ fn pexprefix(P *Parser) Expr {
if !isnumtype(ex.ty) and !ex.ty->is(:Ptr) {
fatal(P, ex.loc, "invalid operand to unary operator %qT (%t)", tok, ex.ty);
}
- if !islvalue(ex) {
+ if !islvalue(&ex) {
err(P, ex.loc, "left operand to prefix %qT operator is not lvalue", tok);
}
if ex.ty.konst {
err(P, ex.loc, "left operand to prefix %qT operator is const", tok);
}
+ if ex.u.#tag == :BitDot {
+ err(P, ex.loc, "cannot apply pre-%srement operator to bitfield value",
+ tok.t == '++' ? "inc" : "dec");
+ }
return {
tok.loc, ex.ty, .u: :UnOp {
tok.t == '++' ? :preinc : :predec, exprdup(P.alloc, ex)
@@ -1805,7 +1912,7 @@ fn pexprefix(P *Parser) Expr {
case lexmatch(P, &tok, '&');
let ex = pexprefix(P);
let ty2 = interntype({ g_targ.ptrsize, .u: :Ptr(ex.ty) });
- if !islvalue(ex) and !(ex.u.#tag == :Symbol and ex.u.Symbol.u.#tag == :Fn)
+ if !islvalue(&ex) and !(ex.u.#tag == :Symbol and ex.u.Symbol.u.#tag == :Fn)
and !(ex.u.#tag == :ZeroIni or ex.u.#tag == :AggIni or ex.u.#tag == :ArrIni) {
err(P, ex.loc, "invalid operand to `&': not an lvalue");
}
@@ -2027,7 +2134,7 @@ fn pexassign(P *Parser) Expr {
}
let rhs = pexcond(P);
switch {
- case !islvalue(ex);
+ case !islvalue(&ex);
err(P, ex.loc, "left operand to assignment is not lvalue");
case (typeof2(ex.ty, rhs.ty) == #null)
and !((ex.ty->is(:Ptr) and rhs.ty->is(:Int)
@@ -2035,6 +2142,8 @@ fn pexassign(P *Parser) Expr {
err(P, ex.loc,
"operands %t and %t to assignment operator %qT have incompatible types",
ex.ty, rhs.ty, tok);
+ case okind != :Set and ex.u.#tag == :BitDot;
+ err(P, ex.loc, "cannot apply compound assignment operator to bitfield value");
case ex.ty.konst;
err(P, ex.loc, "left operand to assignment is const");
}
@@ -2289,7 +2398,7 @@ fn psteuswitch(P *Parser, loc Loc, test Expr) Stmt {
seen->put(c.variant, tok.loc);
if c.fld.ty != #null and lexmatch(P, &tok, '*') {
- if !islvalue(test) {
+ if !islvalue(&test) {
fatal(P, tok.loc, "cannot capture by pointer, test expression is not lvalue");
}
c.captptr = #t;
@@ -2320,7 +2429,7 @@ fn psteuswitch(P *Parser, loc Loc, test Expr) Stmt {
}
- return { loc, :EUSwitch { islvalue(test), test, cs->move(P.alloc), f }};
+ return { loc, :EUSwitch { islvalue(&test), test, cs->move(P.alloc), f }};
}
fn pstcswitch(P *Parser, loc Loc) Stmt {
@@ -2914,6 +3023,11 @@ fn parsedecls(P *Parser, loc Loc, yield DeclYielder, yarg *void, toplevel bool)
{ name, tok.loc, .u: :Tepl(parsetepl(P, tok.loc, :Union, name)) });
}
+ case lexmatch(P, &tok, :kw_bitfield);
+ let name = lexexpect(P, :ident).u.ident;
+ let ty = parsebitfield(P, name);
+ decl = putdecl(P, loc, { name, loc, .u: :Ty(ty) });
+
case lexmatch(P, &tok, :kw_static);
struct Arg {
P *Parser,
diff --git a/src/type.cff b/src/type.cff
index 214bda8..c7e76cc 100644
--- a/src/type.cff
+++ b/src/type.cff
@@ -43,6 +43,9 @@ struct TypeTraits {
case Enum enu;
h = fnv1a_i(h, enu.id);
+
+ case BitF bitf;
+ h = fnv1a_i(h, bitf.id);
}
return h;
@@ -97,6 +100,9 @@ struct TypeTraits {
case Enum enu;
return enu.id == b.u.Enum.id;
+
+ case BitF bitf;
+ return bitf.id == b.u.BitF.id;
}
return #f;
}
diff --git a/test/2.cff b/test/2.cff
new file mode 100644
index 0000000..8b512ea
--- /dev/null
+++ b/test/2.cff
@@ -0,0 +1,17 @@
+import "libc.hff";
+
+bitfield Foo : uint {
+ tag 3,
+ flag (3, 1) bool,
+ num 10 signed,
+}
+
+extern fn main() int {
+ let foo Foo = {};
+ printf("0x%X: %d, %d, %d\n", foo.#raw, foo.tag, foo.flag, foo.num);
+ foo.tag = 3;
+ foo.#raw += 1;
+ foo.flag = #t;
+ foo.num = -2;
+ printf("0x%X: %d, %d, %d\n", foo.#raw, foo.tag, foo.flag, foo.num);
+}
diff --git a/x.ll b/x.ll
deleted file mode 100644
index 84182a2..0000000
--- a/x.ll
+++ /dev/null
@@ -1,309 +0,0 @@
-target triple = "x86_64-pc-linux-gnu"
-%.type.opaque = type opaque
-
-@xs.2 = internal global ptr zeroinitializer
-define internal i32 @icmp.4(ptr %lhs, ptr %rhs, ptr %_) {
-%lhs.0 = alloca ptr store ptr %lhs, ptr %lhs.0
-%rhs.1 = alloca ptr store ptr %rhs, ptr %rhs.1
-%_.2 = alloca ptr store ptr %_, ptr %_.2
- %lhs.3 = alloca i32
- %t1 = load ptr, ptr %lhs.0
- %t0 = bitcast ptr %t1 to ptr
- %t2 = load i32, ptr %t0
- store i32 %t2, ptr %lhs.3
- %rhs.4 = alloca i32
- %t4 = load ptr, ptr %rhs.1
- %t3 = bitcast ptr %t4 to ptr
- %t5 = load i32, ptr %t3
- store i32 %t5, ptr %rhs.4
- %t6 = load i32, ptr %lhs.3
- %t7 = load i32, ptr %rhs.4
- %t8 = sub i32 %t6, %t7
- ret i32 %t8
- ret i32 undef
-}
-define internal void @foo.5() {
- ret void
-}
-define internal void @isort.3(ptr %xs, i64 %n) {
-%xs.0 = alloca ptr store ptr %xs, ptr %xs.0
-%n.1 = alloca i64 store i64 %n, ptr %n.1
- %t1 = load ptr, ptr %xs.0
- %t0 = bitcast ptr %t1 to ptr
- %t2 = load i64, ptr %n.1
- %t3 = sext i32 4 to i64
- call void(ptr, i64, i64, ptr) @qsort(ptr %t0, i64 %t2, i64 %t3, ptr @icmp.4)
- %x.2 = alloca ptr
- store ptr @foo.5, ptr %x.2
- %t4 = load ptr, ptr %x.2
- call ptr %t4()
- ret void
-}
-define internal i64 @length.7(ptr %l) {
-%l.0 = alloca ptr store ptr %l, ptr %l.0
- %n.1 = alloca i64
- store i64 0, ptr %n.1
- %t0 = load ptr, ptr %l.0
- %t1 = load { ptr, i32 }, ptr %t0
- call void({ ptr, i32 }) @ok.6({ ptr, i32 } %t1)
- br label %Cont1
-Cont1: %t2 = bitcast i8 0 to i8 ; NOP
- %t3 = load ptr, ptr %l.0
- %t4 = icmp ne ptr %t3, null
- br i1 %t4, label %Next1, label %Brk1
-Next1: %t5 = bitcast i8 0 to i8 ; NOP
- %t6 = load i64, ptr %n.1
- %t7 = add i64 %t6, 1
- store i64 %t7, ptr %n.1
- %t9 = load ptr, ptr %l.0
- %t10 = getelementptr { ptr, i32 }, ptr %t9, i32 0, i32 0
- %t8 = load ptr, ptr %t10
- store ptr %t8, ptr %l.0
- br label %Cont1
-Brk1: %t11 = bitcast i8 0 to i8 ; NOP
- %t12 = load i64, ptr %n.1
- ret i64 %t12
- ret i64 undef
-}
-define internal void @ok.8({ ptr, i32 } %l) {
-%l.0 = alloca { ptr, i32 } store { ptr, i32 } %l, ptr %l.0
- ret void
-}
-define internal float @mag.9({ float, float } %v) {
-%v.0 = alloca { float, float } store { float, float } %v, ptr %v.0
- %t1 = getelementptr { float, float }, ptr %v.0, i32 0, i32 0
- %t0 = load float, ptr %t1
- %t3 = getelementptr { float, float }, ptr %v.0, i32 0, i32 0
- %t2 = load float, ptr %t3
- %t4 = fmul float %t0, %t2
- %t6 = getelementptr { float, float }, ptr %v.0, i32 0, i32 1
- %t5 = load float, ptr %t6
- %t8 = getelementptr { float, float }, ptr %v.0, i32 0, i32 1
- %t7 = load float, ptr %t8
- %t9 = fmul float %t5, %t7
- %t10 = fadd float %t4, %t9
- %t11 = call float(float) @sqrtf(float %t10)
- ret float %t11
- ret float undef
-}
-define internal void @zero.11(ptr %v) {
-%v.0 = alloca ptr store ptr %v, ptr %v.0
- %t0 = load ptr, ptr %v.0
- %t1 = getelementptr { float, float }, ptr %t0, i32 0, i32 0
- %t2 = sitofp i32 0 to float
- store float %t2, ptr %t1
- %t3 = load ptr, ptr %v.0
- %t4 = getelementptr { float, float }, ptr %t3, i32 0, i32 1
- %t5 = sitofp i32 0 to float
- store float %t5, ptr %t4
- ret void
-}
-define internal { ptr, i64 } @spanz.12(ptr %cstr) {
-%cstr.0 = alloca ptr store ptr %cstr, ptr %cstr.0
- %t0 = load ptr, ptr %cstr.0
- %t1 = sext i32 0 to i64
- %t2 = load ptr, ptr %cstr.0
- %t3 = call i64(ptr) @strlen(ptr %t2)
- %t4 = sub i64 %t3, %t1
- %t5 = insertvalue { ptr, i64 } undef, ptr %t0, 0
- %t6 = insertvalue { ptr, i64 } %t5, i64 %t4, 1
- ret { ptr, i64 } %t6
- ret { ptr, i64 } undef
-}
-define i32 @main(i32 %argc, ptr %argv) {
-%argc.0 = alloca i32 store i32 %argc, ptr %argc.0
-%argv.1 = alloca ptr store ptr %argv, ptr %argv.1
- %colors.2 = alloca [3 x i16]
- %t0 = alloca [3 x i16]
- store [3 x i16] zeroinitializer, ptr %t0
- %t1 = getelementptr [3 x i16], ptr %t0, i32 0, i32 0
- store i16 0, ptr %t1
- %t2 = getelementptr [3 x i16], ptr %t0, i32 0, i32 1
- store i16 1, ptr %t2
- %t3 = getelementptr [3 x i16], ptr %t0, i32 0, i32 2
- store i16 2, ptr %t3
- %t4 = load [3 x i16], ptr %t0
- store [3 x i16] %t4, ptr %colors.2
- %x.3 = alloca { float, float }
- %t5 = alloca { float, float }
- store { float, float } zeroinitializer, ptr %t5
- %t6 = getelementptr { float, float }, ptr %t5, i32 0, i32 1
- %t7 = sitofp i32 1 to float
- store float %t7, ptr %t6
- %t8 = getelementptr { float, float }, ptr %t5, i32 0, i32 0
- %t9 = fptrunc double 2.39999999999999991118216 to float
- store float %t9, ptr %t8
- %t10 = load { float, float }, ptr %t5
- store { float, float } %t10, ptr %x.3
- %p.4 = alloca ptr
- %t11 = bitcast ptr %x.3 to ptr
- store ptr %t11, ptr %p.4
- %t12 = getelementptr [16 x i8], ptr @.str.0, i64 0, i64 0
- %t14 = getelementptr { float, float }, ptr %x.3, i32 0, i32 0
- %t13 = load float, ptr %t14
- %t16 = load ptr, ptr %p.4
- %t17 = getelementptr { float, float }, ptr %t16, i32 0, i32 1
- %t15 = load float, ptr %t17
- %t18 = call i32(ptr, ...) @printf(ptr %t12, float %t13, float %t15)
- %t19 = getelementptr [10 x i8], ptr @.str.1, i64 0, i64 0
- %t20 = load { float, float }, ptr %x.3
- %t21 = call float({ float, float }) @mag.9({ float, float } %t20)
- %t22 = call i32(ptr, ...) @printf(ptr %t19, float %t21)
- call void(ptr) @zero.11(ptr %x.3)
- %t23 = getelementptr [10 x i8], ptr @.str.2, i64 0, i64 0
- %t24 = load { float, float }, ptr %x.3
- %t25 = call float({ float, float }) @mag.9({ float, float } %t24)
- %t26 = call i32(ptr, ...) @printf(ptr %t23, float %t25)
- %x.5 = alloca { i8, i64 }
- %t27 = alloca { i8, i64 }
- store { i8, i64 } zeroinitializer, ptr %t27
- store i8 0, ptr %t27
- %t28 = load { i8, i64 }, ptr %t27
- store { i8, i64 } %t28, ptr %x.5
- %x.6 = alloca { i8, i64 }
- %t29 = alloca { i8, i64 }
- store { i8, i64 } zeroinitializer, ptr %t29
- store i8 1, ptr %t29
- %t30 = bitcast ptr %t29 to ptr
- %t31 = getelementptr i64, ptr %t30, i32 1
- store i64 zeroinitializer, ptr %t31
- %t32 = load { i8, i64 }, ptr %t29
- store { i8, i64 } %t32, ptr %x.6
- %is.7 = alloca [6 x i32]
- %t33 = alloca [6 x i32]
- store [6 x i32] zeroinitializer, ptr %t33
- %t34 = getelementptr [6 x i32], ptr %t33, i32 0, i32 4
- store i32 1, ptr %t34
- %t35 = getelementptr [6 x i32], ptr %t33, i32 0, i32 5
- store i32 2, ptr %t35
- %t36 = getelementptr [6 x i32], ptr %t33, i32 0, i32 0
- store i32 3, ptr %t36
- %t37 = load [6 x i32], ptr %t33
- store [6 x i32] %t37, ptr %is.7
- %t38 = getelementptr [6 x i32], ptr %is.7, i64 0, i64 0
- call void(ptr, i64) @isort.3(ptr %t38, i64 6)
- %i.8 = alloca i64
- store i64 0, ptr %i.8
- br label %Cont1
-Cont1: %t39 = bitcast i8 0 to i8 ; NOP
- %t40 = load i64, ptr %i.8
- %t41 = icmp ult i64 %t40, 6
- %t42 = zext i1 %t41 to i8
- %t43 = icmp ne i8 %t42, 0
- br i1 %t43, label %Next1, label %Brk1
-Next1: %t44 = bitcast i8 0 to i8 ; NOP
- %x.9 = alloca i32
- %t46 = getelementptr [6 x i32], ptr %is.7, i32 0, i32 0
- %t47 = load i64, ptr %i.8
- %t48 = getelementptr i32, ptr %t46, i64 %t47
- %t45 = load i32, ptr %t48
- store i32 %t45, ptr %x.9
- %t49 = getelementptr [4 x i8], ptr @.str.3, i64 0, i64 0
- %t50 = load i32, ptr %x.9
- %t51 = call i32(ptr, ...) @printf(ptr %t49, i32 %t50)
- %t52 = load i64, ptr %i.8
- %t53 = add i64 %t52, 1
- store i64 %t53, ptr %i.8
- br label %Cont1
-Brk1: %t54 = bitcast i8 0 to i8 ; NOP
- %i.10 = alloca i64
- store i64 0, ptr %i.10
- br label %Cont2
-Cont2: %t55 = bitcast i8 0 to i8 ; NOP
- %t56 = load i64, ptr %i.10
- %t57 = icmp ult i64 %t56, 6
- %t58 = zext i1 %t57 to i8
- %t59 = icmp ne i8 %t58, 0
- br i1 %t59, label %Next2, label %Brk2
-Next2: %t60 = bitcast i8 0 to i8 ; NOP
- %x.11 = alloca i32
- %t62 = getelementptr [6 x i32], ptr %is.7, i32 0, i32 0
- %t63 = load i64, ptr %i.10
- %t64 = getelementptr i32, ptr %t62, i64 %t63
- %t61 = load i32, ptr %t64
- store i32 %t61, ptr %x.11
- %t65 = getelementptr [4 x i8], ptr @.str.4, i64 0, i64 0
- %t66 = load i32, ptr %x.11
- %t67 = call i32(ptr, ...) @printf(ptr %t65, i32 %t66)
- %t68 = load i64, ptr %i.10
- %t69 = add i64 %t68, 1
- store i64 %t69, ptr %i.10
- br label %Cont2
-Brk2: %t70 = bitcast i8 0 to i8 ; NOP
- %slice.12 = alloca { ptr, i64 }
- %t71 = sext i32 3 to i64
- %t72 = sext i32 5 to i64
- %t73 = sub i64 %t72, %t71
- %t74 = insertvalue { ptr, i64 } undef, ptr %is.7, 0
- %t75 = insertvalue { ptr, i64 } %t74, i64 %t73, 1
- store { ptr, i64 } %t75, ptr %slice.12
- %t76 = getelementptr [7 x i8], ptr @.str.5, i64 0, i64 0
- %t78 = load { ptr, i64 }, ptr %slice.12
- %t79 = extractvalue { ptr, i64 } %t78, 0
- %t80 = getelementptr i32, ptr %t79, i32 0
- %t77 = load i32, ptr %t80
- %t81 = call i32(ptr, ...) @printf(ptr %t76, i32 %t77)
- %t82 = load { ptr, i64 }, ptr %slice.12
- %t83 = sext i32 1 to i64
- %t84 = sext i32 4 to i64
- %t85 = extractvalue { ptr, i64 } %t82, 0 %t86 = sub i64 %t84, %t83
- %t87 = insertvalue { ptr, i64 } undef, ptr %t85, 0
- %t88 = insertvalue { ptr, i64 } %t87, i64 %t86, 1
- store { ptr, i64 } %t88, ptr %slice.12
- %t89 = getelementptr [7 x i8], ptr @.str.6, i64 0, i64 0
- %t91 = load { ptr, i64 }, ptr %slice.12
- %t92 = extractvalue { ptr, i64 } %t91, 0
- %t93 = getelementptr i32, ptr %t92, i32 0
- %t90 = load i32, ptr %t93
- %t94 = call i32(ptr, ...) @printf(ptr %t89, i32 %t90)
- %t95 = load { ptr, i64 }, ptr %slice.12
- %t96 = extractvalue { ptr, i64 } %t95, 1
- %v.13 = alloca { float, float }
- store { float, float } zeroinitializer, ptr %v.13
- %t97 = getelementptr [18 x i8], ptr @.str.7, i64 0, i64 0
- %t98 = call i32(ptr, ...) @printf(ptr %t97, i64 24)
- %t99 = getelementptr [20 x i8], ptr @.str.8, i64 0, i64 0
- %t100 = call i32(ptr, ...) @printf(ptr %t99, i64 8)
- %t101 = getelementptr [18 x i8], ptr @.str.9, i64 0, i64 0
- %t102 = call i32(ptr, ...) @printf(ptr %t101, i64 8)
- %t103 = getelementptr [14 x i8], ptr @.str.10, i64 0, i64 0
- %t105 = alloca { float }
- store { float } zeroinitializer, ptr %t105
- %t106 = bitcast ptr %t105 to ptr
- store float 1.19999999999999995559108, ptr %t106
- %t107 = bitcast ptr %t105 to ptr %t104 = load i32, ptr %t107
- %t108 = call i32(ptr, ...) @printf(ptr %t103, i32 %t104)
- switch i64 6, label %ISx0 [ i64 0, label %IS0.0 i64 1, label %IS0.0 i64 3, label %IS0.1 ]
-IS0.0: %t109 = icmp ult i64 6, 2
- %t110 = zext i1 %t109 to i8
- br label %ISe0
-IS0.1: br label %ISe0
-ISx0: br label %ISe0
-ISe0: %t111 = bitcast i8 0 to i8 ; NOP
- %t113 = getelementptr [3 x i16], ptr %colors.2, i32 0, i32 0
- %t114 = getelementptr i16, ptr %t113, i32 0
- %t112 = load i16, ptr %t114
- switch i16 %t112, label %ISx0 [ i16 0, label %IS1.0 ]
-IS1.0: br label %ISe1
-ISx1: br label %ISe1
-ISe1: %t115 = bitcast i8 0 to i8 ; NOP
- ret i32 0
- ret i32 undef
-}
-@.str.0 = internal constant [16 x i8] c"v = { %g, %g }\0A\00";
-@.str.1 = internal constant [10 x i8] c"mag = %g\0A\00";
-@.str.2 = internal constant [10 x i8] c"mag = %g\0A\00";
-@.str.3 = internal constant [4 x i8] c"%d\0A\00";
-@.str.4 = internal constant [4 x i8] c"%d\0A\00";
-@.str.5 = internal constant [7 x i8] c"sl %d\0A\00";
-@.str.6 = internal constant [7 x i8] c"sl %d\0A\00";
-@.str.7 = internal constant [18 x i8] c"sizeof(is) = %zu\0A\00";
-@.str.8 = internal constant [20 x i8] c"sizeof *void = %zu\0A\00";
-@.str.9 = internal constant [18 x i8] c"alignof f64= %zu\0A\00";
-@.str.10 = internal constant [14 x i8] c"1.2 -> %#.8x\0A\00";
-declare float @sqrtf(float);
-@stderr = external global ptr
-declare i32 @printf(ptr, ...);
-declare i64 @strlen(ptr);
-@stdin = external global ptr
-declare void @qsort(ptr, i64, i64, ptr);