diff --git a/.github/workflows/perf.yml b/.github/workflows/perf.yml deleted file mode 100644 index cc380f3..0000000 --- a/.github/workflows/perf.yml +++ /dev/null @@ -1,63 +0,0 @@ -name: perf - -on: - workflow_run: - workflows: ['test (ubuntu-latest)'] - types: [completed] - workflow_dispatch: - inputs: - commits: - description: 'commits to plot' - default: '100' - -concurrency: - group: perf - cancel-in-progress: false - -permissions: - contents: write - -jobs: - graph: - if: >- - github.event_name == 'workflow_dispatch' || - (github.event.workflow_run.conclusion == 'success' && - github.event.workflow_run.head_branch == 'main') - runs-on: self-hosted - timeout-minutes: 720 - - steps: - - name: checkout - uses: actions/checkout@v4 - with: - fetch-depth: 0 - submodules: true - ref: ${{ github.event.workflow_run.head_sha || github.ref }} - - - name: python - uses: actions/setup-python@v5 - with: - python-version: '3.12' - - - name: renderer - run: python -m pip install --quiet matplotlib - - - name: restore cache - run: git fetch -q origin perf || echo "no perf branch yet, starting from scratch" - - - name: graph - run: python src/compiler/bench/perf.py "${{ inputs.commits || 100 }}" - - # publish as a single parentless commit, on its own branch - - name: publish - if: github.event_name == 'workflow_run' || github.ref == 'refs/heads/main' - run: | - GIT_INDEX_FILE=$(mktemp) && export GIT_INDEX_FILE - git read-tree --empty - git update-index --add --cacheinfo 100644,"$(git hash-object -w assets/perf.png)",perf.png - git update-index --add --cacheinfo 100644,"$(git hash-object -w assets/perf.tsv)",perf.tsv - git update-index --add --cacheinfo 100644,"$(git hash-object -w assets/compile.png)",compile.png - git update-index --add --cacheinfo 100644,"$(git hash-object -w assets/compile.tsv)",compile.tsv - commit=$(git -c user.name='nella' -c user.email='simontupy64@gmail.com' \ - commit-tree "$(git write-tree)" -m "perf graph for ${GITHUB_SHA:0:12}") - git push -f origin "$commit:refs/heads/perf" diff --git a/shell.nix b/shell.nix index de9d17f..adcd817 100644 --- a/shell.nix +++ b/shell.nix @@ -7,6 +7,6 @@ pkgs.mkShell { pkgs.gcc pkgs.mimalloc pkgs.clang-tools - (pkgs.python3.withPackages (ps: [ ps.matplotlib ])) + pkgs.python3 ]; } diff --git a/src/backend/ir/text_parser.cpp b/src/backend/ir/text_parser.cpp index c505296..77d63af 100644 --- a/src/backend/ir/text_parser.cpp +++ b/src/backend/ir/text_parser.cpp @@ -542,6 +542,8 @@ namespace rat { switch(op) { case Opcode::If: return fn->create(pn.ty, in[0], in[1]); + case Opcode::Switch: + return fn->create(pn.ty, in[0], in[1]); case Opcode::Proj: if(in[0] == fn->getStart() && pn.projIndex == StartNode::controlProjIndex() && startCtrl) return startCtrl; diff --git a/src/backend/pass/emit/x86/x86_encode.cpp b/src/backend/pass/emit/x86/x86_encode.cpp index 5de28cc..3650f36 100644 --- a/src/backend/pass/emit/x86/x86_encode.cpp +++ b/src/backend/pass/emit/x86/x86_encode.cpp @@ -449,6 +449,14 @@ namespace rat { a->patchRel32(skip, a->here()); } + U32 X86EncodePass::blockIdBound(const MachineFunc& f) { + I32 maxId = -1; + for(const MachineBlock& blk : f.blocks) + if(blk.id > maxId) + maxId = blk.id; + return (U32)(maxId + 1); + } + void X86EncodePass::encodeFunction() { // frame slots in [rbp-frameSize, rbp); saves pushed below, total 16-aligned U32 saveBytes = 8u * (U32)calleeSaved.size(); @@ -485,7 +493,7 @@ namespace rat { } omitFrame = !framey && !hasCall; - blockOffset.assign(fn->blocks.size(), 0); + blockOffset.assign(blockIdBound(*fn), 0); prologue(); for(U32 bi = 0; bi < fn->blocks.size(); ++bi) { const MachineBlock& blk = fn->blocks[bi]; diff --git a/src/backend/pass/emit/x86/x86_encode.h b/src/backend/pass/emit/x86/x86_encode.h index 4341962..0a95f61 100644 --- a/src/backend/pass/emit/x86/x86_encode.h +++ b/src/backend/pass/emit/x86/x86_encode.h @@ -31,6 +31,7 @@ namespace rat { void emitGlobal(ObjectFile& obj, const Global* g, U32 ptrBytes); void reset(const MachineFunc& f, const X86FrameLayout& layout, Asm& asm_, List callee); + static U32 blockIdBound(const MachineFunc& f); void encodeFunction(); static Reg toGp(PhysReg p); diff --git a/src/backend/pass/verify.cpp b/src/backend/pass/verify.cpp index d397bb7..d6158ee 100644 --- a/src/backend/pass/verify.cpp +++ b/src/backend/pass/verify.cpp @@ -194,6 +194,21 @@ namespace rat { err(u, "projection index out of range for an If (must be 0 or 1)"); break; } + case Opcode::Switch: { + auto* sw = cast(n); + if(!isCtrl(sw->getControl())) + err(n, "Switch input 0 (control) is not control-typed"); + if(!sw->getSelector()->getType()->isInt()) + err(n, "Switch selector must be an integer"); + if(!t->isTuple() || t->getTupleElementCount() == 0) { + err(n, "Switch type must be a non-empty tuple of control"); + break; + } + for(U32 i = 0, e = t->getTupleElementCount(); i < e; ++i) + if(!t->getTupleElement(i)->isControl()) + err(n, "Switch tuple element " + std::to_string(i) + " must be control"); + break; + } case Opcode::Proj: { auto* p = cast(n); Node* prod = p->getProducer(); @@ -233,8 +248,8 @@ namespace rat { break; } case Opcode::Constant: - if(!t->isInt()) - err(n, "Constant type must be an integer"); + if(!(t->isInt() || t->isFloat())) + err(n, "Constant type must be an integer or a float"); break; case Opcode::Global: { @@ -425,14 +440,9 @@ namespace rat { } break; } - case OpClass::Unary: { - auto* u = cast(n); - if(!t->isInt()) - err(n, "unary operates on a non-integer type"); - if(u->getOperand()->getType() != t) - err(n, "unary result type differs from its operand"); + case OpClass::Unary: + checkUnary(n); break; - } case OpClass::Compare: { auto* c = cast(n); if(!(t->isInt() && t->getIntWidth() == 1)) @@ -441,20 +451,9 @@ namespace rat { err(n, "comparison operands have different types"); break; } - case OpClass::Convert: { - auto* c = cast(n); - const Type* src = c->getOperand()->getType(); - if(!(src->isInt() && t->isInt())) { - err(n, "conversion requires integer source and destination"); - break; - } - U32 sw = src->getIntWidth(), dw = t->getIntWidth(); - if(op == Opcode::Trunc && dw > sw) - err(n, "trunc widens its operand"); - if((op == Opcode::SExt || op == Opcode::ZExt) && dw < sw) - err(n, "extension narrows its operand"); + case OpClass::Convert: + checkConvert(n); break; - } case OpClass::None: break; } @@ -462,6 +461,67 @@ namespace rat { } } + void VerifyPass::FunctionVerifier::checkUnary(Node* n) { + auto* u = cast(n); + const Type* t = n->getType(); + if(n->getOpcode() == Opcode::FNeg) { + if(!t->isFloat()) + err(n, "fneg operates on a non-float type"); + } else if(!t->isInt()) { + err(n, "unary operates on a non-integer type"); + } + if(u->getOperand()->getType() != t) + err(n, "unary result type differs from its operand"); + } + + void VerifyPass::FunctionVerifier::checkConvert(Node* n) { + auto* c = cast(n); + Opcode op = n->getOpcode(); + const Type* t = n->getType(); + const Type* src = c->getOperand()->getType(); + switch(op) { + case Opcode::Trunc: + case Opcode::SExt: + case Opcode::ZExt: { + if(!(src->isInt() && t->isInt())) { + err(n, "conversion requires integer source and destination"); + break; + } + U32 sw = src->getIntWidth(), dw = t->getIntWidth(); + if(op == Opcode::Trunc && dw > sw) + err(n, "trunc widens its operand"); + if((op == Opcode::SExt || op == Opcode::ZExt) && dw < sw) + err(n, "extension narrows its operand"); + break; + } + case Opcode::SIToFP: + case Opcode::UIToFP: + if(!(src->isInt() && t->isFloat())) + err(n, "conversion requires an integer source and a float destination"); + break; + case Opcode::FPToSI: + case Opcode::FPToUI: + if(!(src->isFloat() && t->isInt())) + err(n, "conversion requires a float source and an integer destination"); + break; + case Opcode::FPExt: + case Opcode::FPTrunc: { + if(!(src->isFloat() && t->isFloat())) { + err(n, "conversion requires float source and destination"); + break; + } + U32 sw = src->getFloatWidth(), dw = t->getFloatWidth(); + if(op == Opcode::FPTrunc && dw > sw) + err(n, "fptrunc widens its operand"); + if(op == Opcode::FPExt && dw < sw) + err(n, "fpext narrows its operand"); + break; + } + default: + break; + } + } + void VerifyPass::FunctionVerifier::checkStopReturns() { Node* stop = fn.getStop(); if(!stop) diff --git a/src/backend/pass/verify.h b/src/backend/pass/verify.h index 5fdf2d2..93e5ea7 100644 --- a/src/backend/pass/verify.h +++ b/src/backend/pass/verify.h @@ -37,6 +37,8 @@ namespace rat { B32 checkArity(const Node* n); void checkEdges(Node* n); void checkNode(Node* n); + void checkUnary(Node* n); + void checkConvert(Node* n); void checkStopReturns(); }; private: diff --git a/src/backend/test/roundtrip_switch.rat b/src/backend/test/roundtrip_switch.rat new file mode 100644 index 0000000..a684f4e --- /dev/null +++ b/src/backend/test/roundtrip_switch.rat @@ -0,0 +1,40 @@ +@name parser round-trip: a switch and its case projections survive parse+emit +@passes verify + +@input +func sw(i64) -> i32 { + v0 = start : (ctrl, mem, i64) + v1 = stop : ctrl v10, v12, v14 + v2 = proj : ctrl #0 "ctrl" of v0 + v3 = proj : mem #1 "mem" of v0 + v4 = proj : i64 #2 "arg0" of v0 + v5 = switch : (ctrl, ctrl, ctrl) v2, v4 + v6 = proj : ctrl #0 "case" of v5 + v7 = proj : ctrl #1 "case" of v5 + v8 = proj : ctrl #2 "case" of v5 + v9 = const : i32 10 + v10 = return : ctrl v6, v3, v9 + v11 = const : i32 11 + v12 = return : ctrl v7, v3, v11 + v13 = const : i32 12 + v14 = return : ctrl v8, v3, v13 +} + +@expect +func sw(i64) -> i32 { + v0 = start : (ctrl, mem, i64) + v1 = stop : ctrl v10, v12, v14 + v2 = proj : ctrl #0 "ctrl" of v0 + v3 = proj : mem #1 "mem" of v0 + v4 = proj : i64 #2 "arg0" of v0 + v5 = switch : (ctrl, ctrl, ctrl) v2, v4 + v6 = proj : ctrl #0 "case" of v5 + v7 = proj : ctrl #1 "case" of v5 + v8 = proj : ctrl #2 "case" of v5 + v9 = const : i32 10 + v10 = return : ctrl v6, v3, v9 + v11 = const : i32 11 + v12 = return : ctrl v7, v3, v11 + v13 = const : i32 12 + v14 = return : ctrl v8, v3, v13 +} diff --git a/src/backend/test/runner.cpp b/src/backend/test/runner.cpp index cbc8636..43b5f98 100644 --- a/src/backend/test/runner.cpp +++ b/src/backend/test/runner.cpp @@ -141,6 +141,11 @@ namespace detail { }); if(!ok) return false; + String diags = trim(sink.str()); + if(!diags.empty()) { + err = "pass diagnostics\n " + diags; + return false; + } String actualCanon, expectCanon, cerr; if(!canonicalIR(emitToString(mod), actualCanon, cerr)) { diff --git a/src/backend/test/verify_float.rat b/src/backend/test/verify_float.rat new file mode 100644 index 0000000..f22e8e1 --- /dev/null +++ b/src/backend/test/verify_float.rat @@ -0,0 +1,52 @@ +@name verify: float const, fneg and the six FP conversions are valid IR +@passes verify + +@input +func fverify(f64, i32) -> f64 { + v0 = start : (ctrl, mem, f64, i32) + v1 = stop : ctrl v20 + v2 = proj : ctrl #0 "ctrl" of v0 + v3 = proj : mem #1 "mem" of v0 + v4 = proj : f64 #2 "arg0" of v0 + v5 = proj : i32 #3 "arg1" of v0 + v6 = fneg : f64 v4 + v7 = sitofp : f64 v5 + v8 = uitofp : f64 v5 + v9 = fadd : f64 v6, v7 + v10 = fadd : f64 v9, v8 + v11 = fptrunc : f32 v10 + v12 = fpext : f64 v11 + v13 = fptosi : i32 v12 + v14 = fptoui : i64 v12 + v15 = sitofp : f64 v13 + v16 = uitofp : f64 v14 + v17 = const : f64 4609434218613702656 + v18 = fadd : f64 v15, v16 + v19 = fadd : f64 v18, v17 + v20 = return : ctrl v2, v3, v19 +} + +@expect +func fverify(f64, i32) -> f64 { + v0 = start : (ctrl, mem, f64, i32) + v1 = stop : ctrl v20 + v2 = proj : ctrl #0 "ctrl" of v0 + v3 = proj : mem #1 "mem" of v0 + v4 = proj : f64 #2 "arg0" of v0 + v5 = proj : i32 #3 "arg1" of v0 + v6 = fneg : f64 v4 + v7 = sitofp : f64 v5 + v8 = uitofp : f64 v5 + v9 = fadd : f64 v6, v7 + v10 = fadd : f64 v9, v8 + v11 = fptrunc : f32 v10 + v12 = fpext : f64 v11 + v13 = fptosi : i32 v12 + v14 = fptoui : i64 v12 + v15 = sitofp : f64 v13 + v16 = uitofp : f64 v14 + v17 = const : f64 4609434218613702656 + v18 = fadd : f64 v15, v16 + v19 = fadd : f64 v18, v17 + v20 = return : ctrl v2, v3, v19 +} diff --git a/src/compiler/emit/emit.cpp b/src/compiler/emit/emit.cpp index 8985447..8129bac 100644 --- a/src/compiler/emit/emit.cpp +++ b/src/compiler/emit/emit.cpp @@ -151,7 +151,11 @@ namespace rat::cc { if(isPointer(to) && !isPointer(from) && !isFloating(from)) { if(n->getOpcode() == Opcode::Constant) return fn.constInt(mod.getPtr(), cast(n)->getValue()); - return fn.convert(Opcode::SExt, n, mod.getPtr()); + U32 fromBits = from.bits == 0 ? 32 : from.bits; + Opcode ext = Opcode::SExt; + if(fromBits < lay.ptrBytes * 8 && from.isUnsigned()) + ext = Opcode::ZExt; + return fn.convert(ext, n, mod.getPtr()); } if(isPointer(from) || isPointer(to)) return n; diff --git a/src/compiler/emit/emit.h b/src/compiler/emit/emit.h index 63da2de..a2ea968 100644 --- a/src/compiler/emit/emit.h +++ b/src/compiler/emit/emit.h @@ -376,6 +376,8 @@ namespace rat::cc { const Designator& des, U32& i, U32& cur); + B32 initListIsFlat(const Expr* init); + U32 initArrayCount(CType elem, const Expr* init); U32 arrayInitOuterExtent(CType elem, const Expr* init); B32 resolveArrayIndices(const List& els, const List& des, diff --git a/src/compiler/emit/emit_decl.cpp b/src/compiler/emit/emit_decl.cpp index 2e37b2f..f1a6339 100644 --- a/src/compiler/emit/emit_decl.cpp +++ b/src/compiler/emit/emit_decl.cpp @@ -192,7 +192,7 @@ namespace rat::cc { failArrayUnknownSize(*d.name); return false; } - count = (I64)arrayInitOuterExtent(d.type, d.init); + count = (I64)initArrayCount(d.type, d.init); } U32 elemSize = byteSize(d.type); U32 total = (U32)count * elemSize; @@ -232,7 +232,7 @@ namespace rat::cc { failArrayUnknownSize(*d.name); return false; } - count = (I64)arrayInitOuterExtent(d.type, d.init); + count = (I64)initArrayCount(d.type, d.init); } if(count <= 0) { failArrayCount(); diff --git a/src/compiler/emit/emit_global.cpp b/src/compiler/emit/emit_global.cpp index 65043b9..27f61ba 100644 --- a/src/compiler/emit/emit_global.cpp +++ b/src/compiler/emit/emit_global.cpp @@ -115,7 +115,7 @@ namespace rat::cc { failArrayUnknownSize(*d.name); return false; } - count = (I64)d.init->args.size(); + count = (I64)initArrayCount(d.type, d.init); } U32 elemSize = byteSize(d.type); U32 total = (U32)count * elemSize; @@ -139,15 +139,7 @@ namespace rat::cc { I64 count; if(!validateGlobalArrayLen(d, count, haveLen)) return false; - B32 flat = false; - if(d.init && d.init->kind == ExprKind::InitList) { - flat = true; - for(U32 i = 0; i < d.init->designators.size(); ++i) - if(d.init->designators[i].isSet) { - flat = false; - break; - } - } + B32 flat = initListIsFlat(d.init); if(!haveLen) { if(!d.init || d.init->kind != ExprKind::InitList) { failArrayUnknownSize(*d.name); diff --git a/src/compiler/emit/emit_init.cpp b/src/compiler/emit/emit_init.cpp index 33a4eba..922f4a4 100644 --- a/src/compiler/emit/emit_init.cpp +++ b/src/compiler/emit/emit_init.cpp @@ -45,6 +45,21 @@ namespace rat::cc { return true; } + B32 Emitter::initListIsFlat(const Expr* init) { + if(!init || init->kind != ExprKind::InitList) + return false; + for(U32 i = 0; i < init->designators.size(); ++i) + if(init->designators[i].isSet) + return false; + return true; + } + + U32 Emitter::initArrayCount(CType elem, const Expr* init) { + if(initListIsFlat(init)) + return flatArrayCount(elem, init->args); + return arrayInitOuterExtent(elem, init); + } + U32 Emitter::arrayInitOuterExtent(CType elem, const Expr* init) { const List& els = init->args; U32 rowLen = elem.array ? elem.array->count : 0; diff --git a/src/compiler/lex/preprocess_eval.cpp b/src/compiler/lex/preprocess_eval.cpp index dd2c1a8..273d80b 100644 --- a/src/compiler/lex/preprocess_eval.cpp +++ b/src/compiler/lex/preprocess_eval.cpp @@ -32,7 +32,8 @@ namespace rat::cc { return v; } fail("unexpected token in #if expression"); - ++i; + if(c.kind != Pk::Eof) + ++i; return {}; } diff --git a/src/compiler/parse/ast.cpp b/src/compiler/parse/ast.cpp index 52c7f66..4c1b6af 100644 --- a/src/compiler/parse/ast.cpp +++ b/src/compiler/parse/ast.cpp @@ -373,7 +373,9 @@ namespace rat::cc { for(U32 i = 0; i < fn->params.size(); ++i) { if(i) os << ", "; - os << typeName(fn->params[i].type) << " " << *fn->params[i].name; + os << typeName(fn->params[i].type); + if(fn->params[i].name) + os << " " << *fn->params[i].name; } os << ") -> " << typeName(fn->retType) << "\n"; if(fn->body) diff --git a/src/compiler/parse/parse_init.cpp b/src/compiler/parse/parse_init.cpp index 0678a5a..a719acc 100644 --- a/src/compiler/parse/parse_init.cpp +++ b/src/compiler/parse/parse_init.cpp @@ -88,6 +88,20 @@ namespace rat::cc { return parseAssignment(); } + I64 Parser::castConstValue(I64 v, CType ty) const { + if(!isInteger(ty) || ty.typeofExpr) + return v; + if(ty.bits == 1) + return v != 0; // _Bool keeps the truth value, not the low bit + if(ty.bits >= 64) + return v; + U64 mask = ((U64)1 << ty.bits) - 1; + U64 low = (U64)v & mask; + if(!ty.isUnsigned() && (low & ((U64)1 << (ty.bits - 1)))) + low |= ~mask; // sign-extend + return (I64)low; + } + B32 Parser::evalIntConst(const Expr* e, I64& out) { switch(e->kind) { case ExprKind::IntLit: @@ -152,7 +166,10 @@ namespace rat::cc { return evalIntConst(c ? e->ternary.whenTrue : e->ternary.whenFalse, out); } case ExprKind::Cast: - return evalIntConst(e->cast.operand, out); + if(!evalIntConst(e->cast.operand, out)) + return false; + out = castConstValue(out, e->cast.type); + return true; case ExprKind::Sizeof: if(e->sizeOf.operand) { fail(peek(), "sizeof expression not allowed in constant expression"); diff --git a/src/compiler/parse/parse_struct.cpp b/src/compiler/parse/parse_struct.cpp index 33f26e3..b9344e5 100644 --- a/src/compiler/parse/parse_struct.cpp +++ b/src/compiler/parse/parse_struct.cpp @@ -110,7 +110,7 @@ namespace rat::cc { U32 unitBytes = typeSizeBytes(ft); if(unitBytes == 0) unitBytes = 4; - U32 falign = fieldAlign(ft); + U32 falign = typeAlignBytes(ft); if(memberAlign > falign) falign = memberAlign; U32 unitBits = unitBytes * 8; @@ -171,7 +171,7 @@ namespace rat::cc { if(!acceptTrailingAlignas(memberAlign)) // trailing form: int a __attribute__(...) return false; U64 esize = typeSizeBytes(ft); - U32 falign = fieldAlign(ft); + U32 falign = typeAlignBytes(ft); if(memberAlign > falign) falign = memberAlign; U64 fsize = isArr ? esize * count : esize; diff --git a/src/compiler/parse/parser.h b/src/compiler/parse/parser.h index 94e0ac9..8f25235 100644 --- a/src/compiler/parse/parser.h +++ b/src/compiler/parse/parser.h @@ -177,6 +177,7 @@ namespace rat::cc { B32 parseEnumSpec(CType& out); B32 evalIntConst(const Expr* e, I64& out); B32 tryEvalIntConst(const Expr* e, I64& out); + I64 castConstValue(I64 v, CType ty) const; // struct/union support StructType* complexStruct(CType realType); @@ -189,9 +190,6 @@ namespace rat::cc { B32 startsType(const Token& tok); U64 typeSizeBytes(CType t) const { return typeSize(t, lay.ptrBytes); } U32 typeAlignBytes(CType t) const { return typeAlign(t, lay.ptrBytes); } - U32 fieldAlign(CType t) const { - return isAggregate(t) ? t.strukt->align : (U32)typeSizeBytes(t); - } private: struct DeclSpecs { B32 isStatic = false; diff --git a/src/compiler/test/correctness/custom/const_expr_cast.c b/src/compiler/test/correctness/custom/const_expr_cast.c new file mode 100644 index 0000000..6354df4 --- /dev/null +++ b/src/compiler/test/correctness/custom/const_expr_cast.c @@ -0,0 +1,30 @@ +// expect: 0 +// output: +//| -56 255 4464 5 +//| 2147483647 -56 1 65535 +//| 44 4 2 + +#include + +enum { A = (char)200 }; +enum { B = (unsigned char)-1 }; +enum { C = (short)70000 }; +enum { D = (int)4294967296LL + 5 }; +enum { E = (unsigned)-1 / 2 }; +enum { F = (long)(char)200 }; +enum { G = (_Bool)2 }; +enum { H = (unsigned short)-1 }; + +int arr[(char)200 + 100]; +struct BF { + int f : (char)200 < 0 ? 3 : 5; +}; + +int main(void) { + struct BF b; + b.f = -4; + printf("%d %d %d %d\n", A, B, C, D); + printf("%d %d %d %d\n", E, F, G, H); + printf("%d %d %d\n", (int)(sizeof arr / sizeof arr[0]), (int)sizeof(struct BF), b.f + 6); + return 0; +} diff --git a/src/compiler/test/correctness/custom/int_to_ptr_zext.c b/src/compiler/test/correctness/custom/int_to_ptr_zext.c new file mode 100644 index 0000000..840a220 --- /dev/null +++ b/src/compiler/test/correctness/custom/int_to_ptr_zext.c @@ -0,0 +1,34 @@ +// expect: 0 +// output: +//| 0x80000000 +//| 0xffffffff80000000 +//| 0x80000000 +//| 0x1 +//| 0xffffffffffff8000 + +#include + +unsigned getu(int k) { + return 0x80000000u + (unsigned)k; +} +int geti(int k) { + return (int)0x80000000 + k; +} +_Bool getb(int k) { + return k == 0; +} +short gets(int k) { + return (short)(-32768 + k); +} + +int main(void) { + unsigned u = getu(0); + printf("%p\n", (void *)u); + int i = geti(0); + printf("%p\n", (void *)i); + printf("%p\n", (void *)0x80000000u); + printf("%p\n", (void *)getb(0)); + short s = gets(0); + printf("%p\n", (void *)s); + return 0; +} diff --git a/src/compiler/test/correctness/custom/local_flat_array_init_size.c b/src/compiler/test/correctness/custom/local_flat_array_init_size.c new file mode 100644 index 0000000..567ced4 --- /dev/null +++ b/src/compiler/test/correctness/custom/local_flat_array_init_size.c @@ -0,0 +1,51 @@ +// expect: 0 +// output: +//| 16 16 +//| 16 16 +//| 16 16 +//| 24 24 +//| 24 24 +//| 16 16 +//| 16 16 +//| 32 32 + +#include + +struct P { + int x, y; +}; +struct Q { + int x; + int a[2]; +}; + +struct P g1[] = {1, 2, 3, 4}; +struct P g2[] = {{1, 2}, {3, 4}}; +struct P g3[] = {{1, 2}, 3, 4}; +struct Q g4[] = {1, 2, 3, 4, 5, 6}; +int g5[][2] = {1, 2, 3, 4, 5, 6}; +int g6[][2] = {{1, 2}, {3, 4}}; +struct P g7[] = {1, 2, 3}; +struct P g8[][2] = {1, 2, 3, 4, 5, 6, 7, 8}; + +int main(void) { + struct P a1[] = {1, 2, 3, 4}; + struct P a2[] = {{1, 2}, {3, 4}}; + struct P a3[] = {{1, 2}, 3, 4}; + struct Q a4[] = {1, 2, 3, 4, 5, 6}; + int a5[][2] = {1, 2, 3, 4, 5, 6}; + int a6[][2] = {{1, 2}, {3, 4}}; + struct P a7[] = {1, 2, 3}; + struct P a8[][2] = {1, 2, 3, 4, 5, 6, 7, 8}; + printf("%d %d\n", (int)sizeof g1, (int)sizeof a1); + printf("%d %d\n", (int)sizeof g2, (int)sizeof a2); + printf("%d %d\n", (int)sizeof g3, (int)sizeof a3); + printf("%d %d\n", (int)sizeof g4, (int)sizeof a4); + printf("%d %d\n", (int)sizeof g5, (int)sizeof a5); + printf("%d %d\n", (int)sizeof g6, (int)sizeof a6); + printf("%d %d\n", (int)sizeof g7, (int)sizeof a7); + printf("%d %d\n", (int)sizeof g8, (int)sizeof a8); + if(a1[1].y != 4 || a4[1].a[1] != 6 || a5[2][1] != 6 || a8[1][1].y != 8) + return 1; + return 0; +} diff --git a/src/compiler/test/correctness/custom/struct_member_array_align.c b/src/compiler/test/correctness/custom/struct_member_array_align.c new file mode 100644 index 0000000..210ef55 --- /dev/null +++ b/src/compiler/test/correctness/custom/struct_member_array_align.c @@ -0,0 +1,43 @@ +// expect: 0 +// output: +//| 28 4 4 +//| 18 2 2 +//| 40 8 8 +//| 24 4 +//| 60 4 + +#include + +struct S { + char c; + int a[2][3]; +}; +struct T { + char c; + short m[2][2][2]; +}; +struct U { + char c; + double d[2][2]; +}; +union V { + char c; + int a[2][3]; +}; +struct W { + char c; + struct S s[2]; +}; + +int main(void) { + struct S s; + printf("%d %d %d\n", + (int)sizeof(struct S), + (int)_Alignof(struct S), + (int)((char*)&s.a[0][0] - (char*)&s)); + printf("%d %d %d\n", (int)sizeof(struct T), (int)_Alignof(struct T), (int)__alignof__(short)); + printf("%d %d %d\n", (int)sizeof(struct U), (int)_Alignof(struct U), (int)__alignof__(double)); + printf("%d %d\n", (int)sizeof(union V), (int)_Alignof(union V)); + printf("%d %d\n", (int)sizeof(struct W), (int)_Alignof(struct W)); + return 0; +} diff --git a/src/compiler/test/correctness/custom/switch_unreachable_label.c b/src/compiler/test/correctness/custom/switch_unreachable_label.c new file mode 100644 index 0000000..0a9bf84 --- /dev/null +++ b/src/compiler/test/correctness/custom/switch_unreachable_label.c @@ -0,0 +1,13 @@ +// expect: 1 +// passes: +int x = 1; + +int main(void) { + switch(x) { + x = 2; + lbl: + case 1: + return 1; + } + return 0; +} diff --git a/src/compiler/test/lib.py b/src/compiler/test/lib.py index c9c6a53..801d789 100644 --- a/src/compiler/test/lib.py +++ b/src/compiler/test/lib.py @@ -112,7 +112,8 @@ def compare(self, results): row = "BENCH " + f"{self.name}/{self.level}/{test}".ljust(BENCH_NAME_W) for cc in ("rat", "gcc", "clang"): row += f"{vals[cc]:>{BENCH_VAL_W}g}" if cc in vals else f"{'-':>{BENCH_VAL_W}}" - self.totals[cc] = self.totals.get(cc, 0) + vals.get(cc, 0) + if cc in vals: + self.totals[cc] = self.totals.get(cc, 0) + vals[cc] print(row, flush=True) self.benches += 1 diff --git a/src/compiler/test/run.py b/src/compiler/test/run.py index fe51b1a..3f16555 100755 --- a/src/compiler/test/run.py +++ b/src/compiler/test/run.py @@ -15,15 +15,11 @@ if not os.access(exe, os.X_OK): sys.exit("missing binaries, run make") -names = sys.argv[1:] -bench = "bench" in names -reps = [arg for arg in names if arg.startswith("-r")] -names = [name for name in names if name != "bench" and not name.startswith("-r")] +jobs, _quiet, bench, reps, names = lib.parse_args(sys.argv[1:]) run_ir = not names and not bench if not names: names = sorted(child.name for child in TEST.iterdir() if (child / "run.py").is_file()) -jobs = os.cpu_count() counts = {"PASS": 0, "FAIL": 0, "SKIP": 0, "BENCH": 0} totals = {} @@ -55,7 +51,7 @@ def report(cmd, cwd=None): runner = TEST / name / "run.py" if not runner.is_file(): sys.exit(f"no such suite: {name}") - ok &= report([sys.executable, str(runner), f"-j{jobs}", "-q"] + (["bench"] if bench else []) + reps) + ok &= report([sys.executable, str(runner), f"-j{jobs}", f"-r{reps}", "-q"] + (["bench"] if bench else [])) if bench: if counts["BENCH"]: