diff --git a/src/backend/analysis/address.cpp b/src/backend/analysis/address.cpp index ffb6e93..bc94878 100644 --- a/src/backend/analysis/address.cpp +++ b/src/backend/analysis/address.cpp @@ -5,8 +5,6 @@ #include "ir/opcode.h" #include "ir/type.h" -#include - namespace rat { namespace { // extract a compile-time constant value; false if n is not a constant diff --git a/src/backend/analysis/alias_analysis.h b/src/backend/analysis/alias_analysis.h index 1b55fc2..9d7430d 100644 --- a/src/backend/analysis/alias_analysis.h +++ b/src/backend/analysis/alias_analysis.h @@ -10,6 +10,7 @@ #define RAT_ANALYSIS_ALIASANALYSIS_H #include "core.h" +#include "hash.h" namespace rat { struct Node; @@ -37,16 +38,12 @@ namespace rat { struct MustAliasKeyHash { U64 operator()(const MustAliasKey& k) const { - U64 h = 1469598103934665603ull; // FNV-1a - auto mix = [&](U64 v) { - h ^= v; - h *= 1099511628211ull; - }; - mix(reinterpret_cast(k.base)); - mix((U64)k.constant); - mix((U64)k.size); + U64 h = kFnvBasis; + hashMix(h, reinterpret_cast(k.base)); + hashMix(h, (U64)k.constant); + hashMix(h, (U64)k.size); for(Node* s : k.symbolic) - mix(reinterpret_cast(s)); + hashMix(h, reinterpret_cast(s)); return h; } }; diff --git a/src/backend/ir/text_parser.cpp b/src/backend/ir/text_parser.cpp index 77d63af..d672756 100644 --- a/src/backend/ir/text_parser.cpp +++ b/src/backend/ir/text_parser.cpp @@ -1,6 +1,9 @@ #include "ir/text_parser.h" +#include #include +#include +#include #include "ir/function.h" #include "ir/module.h" diff --git a/src/backend/ir/text_parser.h b/src/backend/ir/text_parser.h index 095b455..bd22fbe 100644 --- a/src/backend/ir/text_parser.h +++ b/src/backend/ir/text_parser.h @@ -4,6 +4,8 @@ #include "core.h" #include "ir/opcode.h" +#include + namespace rat { struct Module; struct Type; diff --git a/src/backend/ir/type.cpp b/src/backend/ir/type.cpp index ef039df..2f86682 100644 --- a/src/backend/ir/type.cpp +++ b/src/backend/ir/type.cpp @@ -1,5 +1,7 @@ #include "ir/type.h" +#include + namespace rat { Type::Type(Kind kind, U32 bits, List elements) : kind(kind), diff --git a/src/backend/ir/type.h b/src/backend/ir/type.h index 627ac1f..5b5290f 100644 --- a/src/backend/ir/type.h +++ b/src/backend/ir/type.h @@ -3,6 +3,8 @@ #include "core.h" +#include + namespace rat { struct Type { enum Kind { diff --git a/src/backend/main.cpp b/src/backend/main.cpp index 42c3d76..141c27f 100644 --- a/src/backend/main.cpp +++ b/src/backend/main.cpp @@ -3,6 +3,8 @@ #include "cli.h" #include "ir/text_parser.h" +#include + using namespace rat; namespace detail { diff --git a/src/backend/pass/emit/graph_emitter.cpp b/src/backend/pass/emit/graph_emitter.cpp index e18ce17..93a483b 100644 --- a/src/backend/pass/emit/graph_emitter.cpp +++ b/src/backend/pass/emit/graph_emitter.cpp @@ -4,6 +4,8 @@ #include "ir/module.h" #include "ir/node.h" +#include + namespace rat { namespace detail { void writeId(std::ostream& os, U32 fnIndex, const Node* n) { diff --git a/src/backend/pass/emit/graph_emitter.h b/src/backend/pass/emit/graph_emitter.h index ad8ebc2..c7df9dc 100644 --- a/src/backend/pass/emit/graph_emitter.h +++ b/src/backend/pass/emit/graph_emitter.h @@ -4,6 +4,8 @@ #include "core.h" #include "pass/pass.h" +#include + namespace rat { struct Function; struct Node; diff --git a/src/backend/pass/emit/text_emitter.cpp b/src/backend/pass/emit/text_emitter.cpp index c98d96f..6630742 100644 --- a/src/backend/pass/emit/text_emitter.cpp +++ b/src/backend/pass/emit/text_emitter.cpp @@ -4,6 +4,8 @@ #include "ir/module.h" #include "ir/node.h" +#include + namespace rat { void TextEmitterPass::comment(std::ostream& os, const C8* text) { os << Green << text << Reset; } diff --git a/src/backend/pass/emit/text_emitter.h b/src/backend/pass/emit/text_emitter.h index ec6e9c4..6c9f361 100644 --- a/src/backend/pass/emit/text_emitter.h +++ b/src/backend/pass/emit/text_emitter.h @@ -4,6 +4,8 @@ #include "core.h" #include "pass/pass.h" +#include + namespace rat { struct Function; struct Module; diff --git a/src/backend/pass/emit/x86/x86_encode.h b/src/backend/pass/emit/x86/x86_encode.h index 0a95f61..f96b4d5 100644 --- a/src/backend/pass/emit/x86/x86_encode.h +++ b/src/backend/pass/emit/x86/x86_encode.h @@ -7,6 +7,8 @@ #include "pass/pass.h" #include "target/x86/x86_asm.h" +#include + namespace rat { struct Global; struct ObjectFile; diff --git a/src/backend/pass/emit/x86/x86_layout.cpp b/src/backend/pass/emit/x86/x86_layout.cpp index a967861..d90b225 100644 --- a/src/backend/pass/emit/x86/x86_layout.cpp +++ b/src/backend/pass/emit/x86/x86_layout.cpp @@ -6,7 +6,7 @@ #include "pass/emit/x86/x86_op.h" namespace rat { - namespace { + namespace detail { B32 isPureTestBlock(const MachineBlock& b) { if(b.insts.empty() || b.insts.size() > 3) return false; @@ -172,15 +172,15 @@ namespace rat { } mf.blocks = std::move(arranged); } - } // namespace + } // namespace detail B32 X86LayoutPass::run(Module& module, MachineModule& mm, const TargetInfo&) { U32 changed = 0; for(const Function* fn : module) { MachineFunc& mf = mm.get(fn); - changed += runOnFunction(mf); - changed += forwardJumpChains(mf); - chainLayout(mf); + changed += detail::runOnFunction(mf); + changed += detail::forwardJumpChains(mf); + detail::chainLayout(mf); } return changed != 0; } diff --git a/src/backend/pass/emit/x86/x86_lower.h b/src/backend/pass/emit/x86/x86_lower.h index 2115a11..a298c8d 100644 --- a/src/backend/pass/emit/x86/x86_lower.h +++ b/src/backend/pass/emit/x86/x86_lower.h @@ -8,8 +8,6 @@ #include "pass/emit/x86/x86_op.h" #include "pass/pass.h" -#include - namespace rat { struct BinaryNode; struct CallNode; diff --git a/src/backend/pass/emit/x86/x86_lower_ops.cpp b/src/backend/pass/emit/x86/x86_lower_ops.cpp index 99929a3..a3243aa 100644 --- a/src/backend/pass/emit/x86/x86_lower_ops.cpp +++ b/src/backend/pass/emit/x86/x86_lower_ops.cpp @@ -1,6 +1,7 @@ #include "pass/emit/x86/x86_lower.h" #include "codegen/machine_function.h" +#include "hash.h" #include "ir/function.h" #include "ir/module.h" #include "ir/node.h" @@ -273,11 +274,9 @@ namespace rat { String X86LowerPass::vecPoolSym(const List& bytes) { C8 buf[48]; - U64 h = 1469598103934665603ull; - for(U8 x : bytes) { - h ^= x; - h *= 1099511628211ull; - } + U64 h = kFnvBasis; + for(U8 x : bytes) + hashMix(h, x); std::snprintf(buf, sizeof buf, "__rat_vec_%016lx", (U64)h); String name(buf); if(!mod->getGlobal(name)) { diff --git a/src/backend/pass/opt/gvn.h b/src/backend/pass/opt/gvn.h index c012858..43bb090 100644 --- a/src/backend/pass/opt/gvn.h +++ b/src/backend/pass/opt/gvn.h @@ -10,6 +10,7 @@ #define RAT_PASS_OPT_GVN_H #include "core.h" +#include "hash.h" #include "pass/pass.h" namespace rat { @@ -32,18 +33,14 @@ namespace rat { struct GVNKeyHash { U64 operator()(const GVNKey& k) const { - U64 h = 1469598103934665603ull; - auto mix = [&](U64 v) { - h ^= v; - h *= 1099511628211ull; - }; - mix(k.op); - mix(k.type); - mix((U64)k.payload); - mix(k.in0); - mix(((U64)k.in1) << 32); + U64 h = kFnvBasis; + hashMix(h, k.op); + hashMix(h, k.type); + hashMix(h, (U64)k.payload); + hashMix(h, k.in0); + hashMix(h, ((U64)k.in1) << 32); if(k.sym) - mix(std::hash{}(*k.sym)); + hashMix(h, std::hash{}(*k.sym)); return (U64)h; } }; diff --git a/src/backend/pass/opt/inline.cpp b/src/backend/pass/opt/inline.cpp index 3fb3ee3..385bfd9 100644 --- a/src/backend/pass/opt/inline.cpp +++ b/src/backend/pass/opt/inline.cpp @@ -1,5 +1,6 @@ #include "pass/opt/inline.h" +#include "hash.h" #include "ir/function.h" #include "ir/module.h" #include "ir/node.h" @@ -236,9 +237,9 @@ namespace rat { // caller version folded with its direct callees' versions U64 InlinePass::quietStamp(const Function& caller, const Info& info) const { - U64 stamp = 14695981039346656037ull ^ caller.getVersion(); + U64 stamp = kFnvBasis ^ caller.getVersion(); for(Info* callee : info.callees) - stamp = stamp * 1099511628211ull + callee->fn->getVersion(); + stamp = stamp * kFnvPrime + callee->fn->getVersion(); return stamp; } diff --git a/src/backend/pass/pass_manager.cpp b/src/backend/pass/pass_manager.cpp index bd5894d..f4c3571 100644 --- a/src/backend/pass/pass_manager.cpp +++ b/src/backend/pass/pass_manager.cpp @@ -3,8 +3,8 @@ #include "ir/module.h" #include -#include #include +#include namespace rat { Pass* PassManager::add(UniquePtr pass) { diff --git a/src/backend/pass/pass_manager.h b/src/backend/pass/pass_manager.h index d9387dd..c115aff 100644 --- a/src/backend/pass/pass_manager.h +++ b/src/backend/pass/pass_manager.h @@ -5,6 +5,7 @@ #include "core.h" #include "pass/pass.h" +#include #include namespace rat { diff --git a/src/backend/pass/pass_registry.cpp b/src/backend/pass/pass_registry.cpp index 5093a84..445bd51 100644 --- a/src/backend/pass/pass_registry.cpp +++ b/src/backend/pass/pass_registry.cpp @@ -1,6 +1,7 @@ #include "pass/pass_registry.h" #include +#include #include "pass/pass.h" #include "string.h" diff --git a/src/backend/pass/pass_registry.h b/src/backend/pass/pass_registry.h index ddfc3f5..4281970 100644 --- a/src/backend/pass/pass_registry.h +++ b/src/backend/pass/pass_registry.h @@ -4,6 +4,8 @@ #include "core.h" #include "pass/pass_manager.h" +#include + namespace rat { UniquePtr createPass(const String& name, std::ostream& out); UniquePtr createMachinePass(const String& name, std::ostream& out); diff --git a/src/backend/pass/verify.cpp b/src/backend/pass/verify.cpp index d6158ee..a5be31f 100644 --- a/src/backend/pass/verify.cpp +++ b/src/backend/pass/verify.cpp @@ -5,6 +5,8 @@ #include "ir/node.h" #include "ir/type.h" +#include + namespace rat { VerifyPass::FunctionVerifier::FunctionVerifier(const Function& fn, List& e) : fn(fn), diff --git a/src/backend/pass/verify.h b/src/backend/pass/verify.h index 93e5ea7..b8a0ec3 100644 --- a/src/backend/pass/verify.h +++ b/src/backend/pass/verify.h @@ -4,6 +4,8 @@ #include "core.h" #include "pass/pass.h" +#include + namespace rat { struct Function; struct Module; diff --git a/src/backend/target/object_file.h b/src/backend/target/object_file.h index 63cf20c..71df059 100644 --- a/src/backend/target/object_file.h +++ b/src/backend/target/object_file.h @@ -5,6 +5,8 @@ #include "target/target.h" +#include + namespace rat { enum class RelocKind : U32 { Abs64 = 1, // absolute 64-bit address (S + A) diff --git a/src/backend/target/x86/x86_coff.cpp b/src/backend/target/x86/x86_coff.cpp index bd92305..b139d80 100644 --- a/src/backend/target/x86/x86_coff.cpp +++ b/src/backend/target/x86/x86_coff.cpp @@ -2,6 +2,8 @@ #include "byte_io.h" +#include + namespace rat { namespace detail { constexpr U16 IMAGE_FILE_MACHINE_AMD64 = 0x8664; diff --git a/src/backend/target/x86/x86_elf.cpp b/src/backend/target/x86/x86_elf.cpp index a073052..19de430 100644 --- a/src/backend/target/x86/x86_elf.cpp +++ b/src/backend/target/x86/x86_elf.cpp @@ -3,6 +3,8 @@ #include "byte_io.h" #include "elf_file.h" +#include + namespace rat { namespace detail { constexpr U8 kElfMag[4] = {0x7f, 'E', 'L', 'F'}; diff --git a/src/backend/test/runner.cpp b/src/backend/test/runner.cpp index 43b5f98..7001c48 100644 --- a/src/backend/test/runner.cpp +++ b/src/backend/test/runner.cpp @@ -3,6 +3,7 @@ #include "string.h" #include "test_harness.h" #include +#include #include "rat.h" diff --git a/src/base/cli.h b/src/base/cli.h index 4aa7fc1..faedf4d 100644 --- a/src/base/cli.h +++ b/src/base/cli.h @@ -1,7 +1,9 @@ #ifndef RAT_BASE_CLI_H #define RAT_BASE_CLI_H +#include #include +#include #include "core.h" #include "git_hash.h" diff --git a/src/base/core.h b/src/base/core.h index 0a5fe88..95e89f7 100644 --- a/src/base/core.h +++ b/src/base/core.h @@ -3,16 +3,12 @@ #include #include -#include #include -#include #include #include -#include #include -#include -#include #include +#include #include #include #include diff --git a/src/base/hash.h b/src/base/hash.h new file mode 100644 index 0000000..7ed68c7 --- /dev/null +++ b/src/base/hash.h @@ -0,0 +1,17 @@ +#ifndef RAT_BASE_HASH_H +#define RAT_BASE_HASH_H + +#include "core.h" + +namespace rat { + // FNV-1a 64-bit + constexpr U64 kFnvBasis = 14695981039346656037ull; + constexpr U64 kFnvPrime = 1099511628211ull; + + inline void hashMix(U64& h, U64 v) { + h ^= v; + h *= kFnvPrime; + } +} // namespace rat + +#endif diff --git a/src/base/string.h b/src/base/string.h index 6abb7d8..24137c3 100644 --- a/src/base/string.h +++ b/src/base/string.h @@ -3,6 +3,9 @@ #include "core.h" +#include +#include + namespace rat { inline B32 readAll(std::istream& in, String& out) { std::ostringstream ss; diff --git a/src/base/test_harness.cpp b/src/base/test_harness.cpp index 14786ea..92e9297 100644 --- a/src/base/test_harness.cpp +++ b/src/base/test_harness.cpp @@ -1,7 +1,9 @@ #include "test_harness.h" #include +#include #include +#include #include #include diff --git a/src/compiler/compile.h b/src/compiler/compile.h index 8c445f8..bc8e94b 100644 --- a/src/compiler/compile.h +++ b/src/compiler/compile.h @@ -3,6 +3,8 @@ #include "core.h" +#include + namespace rat { struct MachinePass; struct Pass; diff --git a/src/compiler/emit/emit.h b/src/compiler/emit/emit.h index a2ea968..fbf85cc 100644 --- a/src/compiler/emit/emit.h +++ b/src/compiler/emit/emit.h @@ -10,6 +10,29 @@ namespace rat::cc { namespace detail { U32 alignedChunkWidth(U32 offset, U32 size); + + // shared child traversal for AST scans + struct AstWalk { + virtual ~AstWalk() = default; + + // child edges this walk follows + B32 sizeofOperand = false; // operand of sizeof + B32 alignOfOperand = false; // operand of _Alignof + B32 compoundLitInit = false; // initializer of a compound literal + B32 stmtExprBody = false; // body of a statement expression + B32 exprChildren = false; // expressions a statement holds + B32 nestedSwitch = false; // children of a nested switch + B32 forInit = false; // initializer statement of a for + + // per-node action, run before the children + virtual B32 onExpr(const Expr*) { return true; } + virtual B32 onStmt(const Stmt*) { return true; } + }; + + B32 walkExpr(AstWalk& w, const Expr* e); + B32 walkExprChildren(AstWalk& w, const Expr* e); + B32 walkStmt(AstWalk& w, const Stmt* s); + B32 walkStmtChildren(AstWalk& w, const Stmt* s); } // namespace detail B32 builtinReturnType(const String& name, U32 longBits, CType& out); @@ -300,16 +323,13 @@ namespace rat::cc { Set memVars; void collectAddrTaken(const Stmt* s); - void collectAddrTakenExpr(const Expr* e); U32 strCounter = 0; Map strPool; // string-literal bytes -> interned symbol Map labelBlocks; void collectLabels(Function& fn, const Stmt* s); - void collectLabelsInExpr(Function& fn, const Expr* e); static B32 containsLabel(const Stmt* s); - static B32 containsLabelInExpr(const Expr* e); static B32 containsSwitchCase(const Stmt* s); B32 registerGlobals(const TransUnit& unit); diff --git a/src/compiler/emit/emit_const.cpp b/src/compiler/emit/emit_const.cpp index 20aca9f..db8d7dc 100644 --- a/src/compiler/emit/emit_const.cpp +++ b/src/compiler/emit/emit_const.cpp @@ -3,6 +3,8 @@ namespace rat::cc { static I64 narrowToType(I64 v, CType ty) { U32 bits = isPointer(ty) ? 64 : ty.bits; + if(bits == 1) + return v != 0; if(bits >= 64) return v; U64 mask = ((U64)1 << bits) - 1; diff --git a/src/compiler/emit/emit_global.cpp b/src/compiler/emit/emit_global.cpp index 27f61ba..2a86218 100644 --- a/src/compiler/emit/emit_global.cpp +++ b/src/compiler/emit/emit_global.cpp @@ -179,15 +179,19 @@ namespace rat::cc { List init; if(d.init && d.init->kind == ExprKind::StrLit) { - if(d.type.ptr != 0 || d.type.bits != 8) { + U32 charWidth = d.init->str.isWide ? d.init->str.charSize : 1u; + if(d.type.ptr != 0 || d.type.bits != charWidth * 8) { failStringNeedsCharArray(); return false; } const String& bytes = *d.init->str.bytes; + I64 nchars = (I64)bytes.size() / (I64)charWidth; if(!haveLen) - count = (I64)bytes.size() + 1; - for(I64 i = 0; i < count; ++i) - init.push_back(i < (I64)bytes.size() ? (U8)bytes[(U32)i] : 0); + count = nchars + 1; + init.assign((U32)count * elemSize, 0); + for(I64 i = 0; i < nchars && i < count; ++i) + for(U32 k = 0; k < charWidth; ++k) + init[(U32)(i * elemSize) + k] = (U8)bytes[(U32)(i * charWidth + k)]; } else if(d.init && d.init->kind == ExprKind::InitList) { const List& els = d.init->args; const List& des = d.init->designators; diff --git a/src/compiler/emit/emit_overflow.cpp b/src/compiler/emit/emit_overflow.cpp index 78274ee..991c867 100644 --- a/src/compiler/emit/emit_overflow.cpp +++ b/src/compiler/emit/emit_overflow.cpp @@ -1,7 +1,7 @@ #include "emit/emit.h" namespace rat::cc { - namespace { + namespace detail { struct OvfForm { Opcode op = Opcode::Add; B32 predicate = false; @@ -87,11 +87,11 @@ namespace rat::cc { t.set(CType::Unsigned, uns); return t; } - } // namespace + } // namespace detail Emitter::Wide Emitter::wideExtend(Function& fn, Node* v, CType from) { Wide r; - r.lo = convert(fn, v, from, ctWide(from.isUnsigned())); + r.lo = convert(fn, v, from, detail::ctWide(from.isUnsigned())); if(from.isUnsigned()) r.hi = fn.constInt(r.lo->getType(), 0); else @@ -180,8 +180,8 @@ namespace rat::cc { B32 Emitter::emitOverflowBuiltin(Function& fn, const Expr* e, Value& out) { const String& b = *e->call.callee; - OvfForm f; - if(!parseOverflowName(b, lay.longBits, f)) + detail::OvfForm f; + if(!detail::parseOverflowName(b, lay.longBits, f)) return false; if(e->args.size() != 3) { fail("'" + b + "' expects three arguments"); @@ -224,9 +224,9 @@ namespace rat::cc { Node* fits; Node* value; - B32 sgn = exactInSigned64(f.op, at, bt); - if(sgn || exactInUnsigned64(f.op, at, bt)) { - CType w = ctWide(!sgn); + B32 sgn = detail::exactInSigned64(f.op, at, bt); + if(sgn || detail::exactInUnsigned64(f.op, at, bt)) { + CType w = detail::ctWide(!sgn); Node* r = fn.binary(f.op, convert(fn, an, at, w), convert(fn, bn, bt, w)); fits = fitsIn64(fn, r, rt, sgn); value = convert(fn, r, w, rt); @@ -239,7 +239,7 @@ namespace rat::cc { else r = wideAddSub(fn, wa, wb, f.op == Opcode::Sub); fits = wideFits(fn, r, rt); - value = convert(fn, r.lo, ctWide(true), rt); + value = convert(fn, r.lo, detail::ctWide(true), rt); } if(!f.predicate) fn.store(a2.node, value); diff --git a/src/compiler/emit/emit_scan.cpp b/src/compiler/emit/emit_scan.cpp index 2f27f33..434acfe 100644 --- a/src/compiler/emit/emit_scan.cpp +++ b/src/compiler/emit/emit_scan.cpp @@ -1,323 +1,249 @@ #include "emit/emit.h" namespace rat::cc { - void Emitter::collectAddrTakenExpr(const Expr* e) { - if(!e) - return; - switch(e->kind) { - case ExprKind::Unary: - if(e->unary.op == ExprOp::Addr && e->unary.operand->kind == ExprKind::Ident) - memVars.insert(*e->unary.operand->ident.name); - collectAddrTakenExpr(e->unary.operand); - return; - case ExprKind::Binary: - collectAddrTakenExpr(e->binary.lhs); - collectAddrTakenExpr(e->binary.rhs); - return; - case ExprKind::Ternary: - collectAddrTakenExpr(e->ternary.cond); - collectAddrTakenExpr(e->ternary.whenTrue); - collectAddrTakenExpr(e->ternary.whenFalse); - return; - case ExprKind::Comma: - collectAddrTakenExpr(e->comma.lhs); - collectAddrTakenExpr(e->comma.rhs); - return; - case ExprKind::Cast: - collectAddrTakenExpr(e->cast.operand); - return; - case ExprKind::Call: - if(lay.win64VaList && e->call.callee && !e->args.empty() && - e->args[0]->kind == ExprKind::Ident) { - const String& b = *e->call.callee; - if(b == "__builtin_va_start" || b == "__builtin_va_end" || b == "__builtin_va_copy") - memVars.insert(*e->args[0]->ident.name); + namespace detail { + B32 walkExprChildren(AstWalk& w, const Expr* e) { + switch(e->kind) { + case ExprKind::Unary: + return walkExpr(w, e->unary.operand); + case ExprKind::Binary: + return walkExpr(w, e->binary.lhs) && walkExpr(w, e->binary.rhs); + case ExprKind::Ternary: + return walkExpr(w, e->ternary.cond) && walkExpr(w, e->ternary.whenTrue) && + walkExpr(w, e->ternary.whenFalse); + case ExprKind::Comma: + return walkExpr(w, e->comma.lhs) && walkExpr(w, e->comma.rhs); + case ExprKind::Cast: + return walkExpr(w, e->cast.operand); + case ExprKind::Sizeof: + return !w.sizeofOperand || walkExpr(w, e->sizeOf.operand); + case ExprKind::AlignOf: + return !w.alignOfOperand || walkExpr(w, e->sizeOf.operand); + case ExprKind::Member: + return walkExpr(w, e->member.base); + case ExprKind::Call: + return walkExpr(w, e->call.target); + case ExprKind::VaArg: + return walkExpr(w, e->vaArg.ap); + case ExprKind::CompoundLit: + return !w.compoundLitInit || walkExpr(w, e->compound.init); + case ExprKind::StmtExpr: + return !w.stmtExprBody || walkStmt(w, e->stmtExpr.body); + default: + return true; } - collectAddrTakenExpr(e->call.target); - for(const Expr* arg : e->args) - collectAddrTakenExpr(arg); - return; - case ExprKind::Member: - collectAddrTakenExpr(e->member.base); - return; - case ExprKind::InitList: - for(const Expr* el : e->args) - collectAddrTakenExpr(el); - return; - case ExprKind::CompoundLit: - collectAddrTakenExpr(e->compound.init); - return; - case ExprKind::VaArg: - if(lay.win64VaList && e->vaArg.ap->kind == ExprKind::Ident) - memVars.insert(*e->vaArg.ap->ident.name); - collectAddrTakenExpr(e->vaArg.ap); - return; - case ExprKind::StmtExpr: - collectAddrTaken(e->stmtExpr.body); - return; - default: - return; - } - } - - void Emitter::collectAddrTaken(const Stmt* s) { - if(!s) - return; - switch(s->kind) { - case StmtKind::Compound: - for(const Stmt* child : s->body) - collectAddrTaken(child); - return; - case StmtKind::Decl: - for(const Declarator& d : s->decls) - collectAddrTakenExpr(d.init); - return; - case StmtKind::If: - collectAddrTakenExpr(s->expr); - collectAddrTaken(s->thenBody); - collectAddrTaken(s->elseBody); - return; - case StmtKind::While: - case StmtKind::DoWhile: - case StmtKind::Switch: - collectAddrTakenExpr(s->expr); - collectAddrTaken(s->thenBody); - return; - case StmtKind::For: - collectAddrTaken(s->forInit); - collectAddrTakenExpr(s->expr); - collectAddrTakenExpr(s->forPost); - collectAddrTaken(s->thenBody); - return; - case StmtKind::Label: - case StmtKind::Default: - collectAddrTaken(s->thenBody); - return; - case StmtKind::Case: - collectAddrTakenExpr(s->expr); - collectAddrTaken(s->thenBody); - return; - case StmtKind::Return: - case StmtKind::Expr: - collectAddrTakenExpr(s->expr); - return; - default: - return; - } - } - - void Emitter::collectLabelsInExpr(Function& fn, const Expr* e) { - if(!e) - return; - switch(e->kind) { - case ExprKind::StmtExpr: - collectLabels(fn, e->stmtExpr.body); - break; - case ExprKind::Unary: - collectLabelsInExpr(fn, e->unary.operand); - break; - case ExprKind::Binary: - collectLabelsInExpr(fn, e->binary.lhs); - collectLabelsInExpr(fn, e->binary.rhs); - break; - case ExprKind::Ternary: - collectLabelsInExpr(fn, e->ternary.cond); - collectLabelsInExpr(fn, e->ternary.whenTrue); - collectLabelsInExpr(fn, e->ternary.whenFalse); - break; - case ExprKind::Comma: - collectLabelsInExpr(fn, e->comma.lhs); - collectLabelsInExpr(fn, e->comma.rhs); - break; - case ExprKind::Cast: - collectLabelsInExpr(fn, e->cast.operand); - break; - case ExprKind::Sizeof: - case ExprKind::AlignOf: - collectLabelsInExpr(fn, e->sizeOf.operand); - break; - case ExprKind::Member: - collectLabelsInExpr(fn, e->member.base); - break; - case ExprKind::Call: - collectLabelsInExpr(fn, e->call.target); - break; - case ExprKind::VaArg: - collectLabelsInExpr(fn, e->vaArg.ap); - break; - default: - break; } - for(const Expr* a : e->args) - collectLabelsInExpr(fn, a); - } - void Emitter::collectLabels(Function& fn, const Stmt* s) { - if(!s) - return; - switch(s->kind) { - case StmtKind::Label: - if(!labelBlocks.count(*s->label)) - labelBlocks[*s->label] = fn.createLoopHeader("label." + *s->label); - collectLabels(fn, s->thenBody); - return; - case StmtKind::Compound: - for(const Stmt* child : s->body) - collectLabels(fn, child); - return; - case StmtKind::If: - collectLabelsInExpr(fn, s->expr); - collectLabels(fn, s->thenBody); - collectLabels(fn, s->elseBody); - return; - case StmtKind::While: - case StmtKind::DoWhile: - case StmtKind::Switch: - collectLabelsInExpr(fn, s->expr); - collectLabels(fn, s->thenBody); - return; - case StmtKind::For: - collectLabels(fn, s->forInit); - collectLabelsInExpr(fn, s->expr); - collectLabelsInExpr(fn, s->forPost); - collectLabels(fn, s->thenBody); - return; - case StmtKind::Expr: - case StmtKind::Return: - collectLabelsInExpr(fn, s->expr); - return; - case StmtKind::Case: - collectLabelsInExpr(fn, s->expr); - collectLabels(fn, s->thenBody); - return; - case StmtKind::Default: - collectLabels(fn, s->thenBody); - return; - case StmtKind::Decl: - for(const Declarator& d : s->decls) - collectLabelsInExpr(fn, d.init); - return; - default: - return; + // args holds the call arguments and the initializer-list elements; it is + // empty for every other kind, so it is walked last for all of them. + B32 walkExpr(AstWalk& w, const Expr* e) { + if(!e) + return true; + if(!w.onExpr(e)) + return false; + if(!walkExprChildren(w, e)) + return false; + for(const Expr* a : e->args) + if(!walkExpr(w, a)) + return false; + return true; } - } - B32 Emitter::containsLabelInExpr(const Expr* e) { - if(!e) - return false; - switch(e->kind) { - case ExprKind::StmtExpr: - if(containsLabel(e->stmtExpr.body)) - return true; - break; - case ExprKind::Unary: - if(containsLabelInExpr(e->unary.operand)) - return true; - break; - case ExprKind::Binary: - if(containsLabelInExpr(e->binary.lhs) || containsLabelInExpr(e->binary.rhs)) + B32 walkStmtChildren(AstWalk& w, const Stmt* s) { + switch(s->kind) { + case StmtKind::Compound: + for(const Stmt* child : s->body) + if(!walkStmt(w, child)) + return false; return true; - break; - case ExprKind::Ternary: - if(containsLabelInExpr(e->ternary.cond) || containsLabelInExpr(e->ternary.whenTrue) || - containsLabelInExpr(e->ternary.whenFalse)) + case StmtKind::Decl: + if(!w.exprChildren) + return true; + for(const Declarator& d : s->decls) + if(!walkExpr(w, d.init)) + return false; return true; - break; - case ExprKind::Comma: - if(containsLabelInExpr(e->comma.lhs) || containsLabelInExpr(e->comma.rhs)) + case StmtKind::If: + if(w.exprChildren && !walkExpr(w, s->expr)) + return false; + return walkStmt(w, s->thenBody) && walkStmt(w, s->elseBody); + case StmtKind::Switch: + if(!w.nestedSwitch) + return true; + if(w.exprChildren && !walkExpr(w, s->expr)) + return false; + return walkStmt(w, s->thenBody); + case StmtKind::While: + case StmtKind::DoWhile: + case StmtKind::Case: + if(w.exprChildren && !walkExpr(w, s->expr)) + return false; + return walkStmt(w, s->thenBody); + case StmtKind::For: + if(w.forInit && !walkStmt(w, s->forInit)) + return false; + if(w.exprChildren && (!walkExpr(w, s->expr) || !walkExpr(w, s->forPost))) + return false; + return walkStmt(w, s->thenBody); + case StmtKind::Label: + case StmtKind::Default: + return walkStmt(w, s->thenBody); + case StmtKind::Return: + case StmtKind::Expr: + return !w.exprChildren || walkExpr(w, s->expr); + default: return true; - break; - case ExprKind::Cast: - if(containsLabelInExpr(e->cast.operand)) + } + } + + B32 walkStmt(AstWalk& w, const Stmt* s) { + if(!s) return true; - break; - case ExprKind::Sizeof: - case ExprKind::AlignOf: - if(containsLabelInExpr(e->sizeOf.operand)) + if(!w.onStmt(s)) + return false; + return walkStmtChildren(w, s); + } + + // &x, and the win64 va_list builtins, force x into memory + struct AddrTakenWalk final : AstWalk { + AddrTakenWalk(Set& vars, B32 win64Va) + : memVars(vars), + win64VaList(win64Va) { + compoundLitInit = true; + stmtExprBody = true; + exprChildren = true; + nestedSwitch = true; + forInit = true; + } + B32 onExpr(const Expr* e) override; + void noteVaBuiltin(const Expr* e); + Set& memVars; + B32 win64VaList; + }; + + void AddrTakenWalk::noteVaBuiltin(const Expr* e) { + if(!win64VaList || !e->call.callee || e->args.empty() || e->args[0]->kind != ExprKind::Ident) + return; + const String& b = *e->call.callee; + if(b == "__builtin_va_start" || b == "__builtin_va_end" || b == "__builtin_va_copy") + memVars.insert(*e->args[0]->ident.name); + } + + B32 AddrTakenWalk::onExpr(const Expr* e) { + switch(e->kind) { + case ExprKind::Unary: + if(e->unary.op == ExprOp::Addr && e->unary.operand->kind == ExprKind::Ident) + memVars.insert(*e->unary.operand->ident.name); return true; - break; - case ExprKind::Member: - if(containsLabelInExpr(e->member.base)) + case ExprKind::Call: + noteVaBuiltin(e); return true; - break; - case ExprKind::Call: - if(containsLabelInExpr(e->call.target)) + case ExprKind::VaArg: + if(win64VaList && e->vaArg.ap->kind == ExprKind::Ident) + memVars.insert(*e->vaArg.ap->ident.name); return true; - break; - case ExprKind::VaArg: - if(containsLabelInExpr(e->vaArg.ap)) + default: return true; - break; - default: - break; + } + } + + struct LabelWalkBase : AstWalk { + LabelWalkBase() { + sizeofOperand = true; + alignOfOperand = true; + stmtExprBody = true; + exprChildren = true; + nestedSwitch = true; + forInit = true; + } + }; + + struct LabelBlockWalk final : LabelWalkBase { + LabelBlockWalk(Function& func, Map& blocks) + : fn(func), + labelBlocks(blocks) {} + B32 onStmt(const Stmt* s) override; + Function& fn; + Map& labelBlocks; + }; + + B32 LabelBlockWalk::onStmt(const Stmt* s) { + if(s->kind == StmtKind::Label && !labelBlocks.count(*s->label)) + labelBlocks[*s->label] = fn.createLoopHeader("label." + *s->label); + return true; } - for(const Expr* a : e->args) - if(containsLabelInExpr(a)) + + struct HasLabelWalk final : LabelWalkBase { + B32 onStmt(const Stmt* s) override { return s->kind != StmtKind::Label; } + }; + + struct RefersToWalk final : AstWalk { + explicit RefersToWalk(const String& n) + : name(n) { + sizeofOperand = true; + compoundLitInit = true; + } + B32 onExpr(const Expr* e) override; + const String& name; + }; + + B32 RefersToWalk::onExpr(const Expr* e) { + if(e->kind != ExprKind::Ident) return true; - return false; - } + return !e->ident.name || *e->ident.name != name; + } - B32 Emitter::containsLabel(const Stmt* s) { - if(!s) - return false; - switch(s->kind) { - case StmtKind::Label: + struct HasSwitchCaseWalk final : AstWalk { + B32 onStmt(const Stmt* s) override { + return s->kind != StmtKind::Case && s->kind != StmtKind::Default; + } + }; + + struct SwitchCaseWalk final : AstWalk { + SwitchCaseWalk(List& list, const Stmt*& defStmt) + : cases(list), + def(defStmt) {} + B32 onStmt(const Stmt* s) override; + List& cases; + const Stmt*& def; + }; + + B32 SwitchCaseWalk::onStmt(const Stmt* s) { + if(s->kind == StmtKind::Case) + cases.push_back(s); + else if(s->kind == StmtKind::Default) + def = s; return true; - case StmtKind::Compound: - for(const Stmt* child : s->body) - if(containsLabel(child)) - return true; - return false; - case StmtKind::If: - return containsLabelInExpr(s->expr) || containsLabel(s->thenBody) || - containsLabel(s->elseBody); - case StmtKind::While: - case StmtKind::DoWhile: - case StmtKind::Switch: - return containsLabelInExpr(s->expr) || containsLabel(s->thenBody); - case StmtKind::For: - return containsLabel(s->forInit) || containsLabelInExpr(s->expr) || - containsLabelInExpr(s->forPost) || containsLabel(s->thenBody); - case StmtKind::Expr: - case StmtKind::Return: - return containsLabelInExpr(s->expr); - case StmtKind::Case: - return containsLabelInExpr(s->expr) || containsLabel(s->thenBody); - case StmtKind::Default: - return containsLabel(s->thenBody); - case StmtKind::Decl: - for(const Declarator& d : s->decls) - if(containsLabelInExpr(d.init)) - return true; - return false; - default: - return false; } + } // namespace detail + + void Emitter::collectAddrTaken(const Stmt* s) { + detail::AddrTakenWalk w(memVars, lay.win64VaList); + detail::walkStmt(w, s); + } + + void Emitter::collectLabels(Function& fn, const Stmt* s) { + detail::LabelBlockWalk w(fn, labelBlocks); + detail::walkStmt(w, s); + } + + B32 Emitter::containsLabel(const Stmt* s) { + detail::HasLabelWalk w; + return !detail::walkStmt(w, s); } B32 Emitter::containsSwitchCase(const Stmt* s) { - if(!s) - return false; - switch(s->kind) { - case StmtKind::Case: - case StmtKind::Default: - return true; - case StmtKind::Label: - return containsSwitchCase(s->thenBody); - case StmtKind::Compound: - for(const Stmt* child : s->body) - if(containsSwitchCase(child)) - return true; - return false; - case StmtKind::If: - return containsSwitchCase(s->thenBody) || containsSwitchCase(s->elseBody); - case StmtKind::While: - case StmtKind::DoWhile: - return containsSwitchCase(s->thenBody); - case StmtKind::For: - return containsSwitchCase(s->thenBody); - default: - return false; - } + detail::HasSwitchCaseWalk w; + return !detail::walkStmt(w, s); + } + + void Emitter::collectSwitchCases(const Stmt* s, List& cases, const Stmt*& def) { + detail::SwitchCaseWalk w(cases, def); + detail::walkStmt(w, s); + } + + B32 Emitter::exprRefersTo(const Expr* e, const String& name) const { + detail::RefersToWalk w(name); + return !detail::walkExpr(w, e); } } // namespace rat::cc diff --git a/src/compiler/emit/emit_stmt.cpp b/src/compiler/emit/emit_stmt.cpp index fc32e38..825ec2d 100644 --- a/src/compiler/emit/emit_stmt.cpp +++ b/src/compiler/emit/emit_stmt.cpp @@ -163,79 +163,6 @@ namespace rat::cc { return true; } - B32 Emitter::exprRefersTo(const Expr* e, const String& name) const { - if(!e) - return false; - switch(e->kind) { - case ExprKind::Ident: - return e->ident.name && *e->ident.name == name; - case ExprKind::Unary: - return exprRefersTo(e->unary.operand, name); - case ExprKind::Binary: - return exprRefersTo(e->binary.lhs, name) || exprRefersTo(e->binary.rhs, name); - case ExprKind::Ternary: - return exprRefersTo(e->ternary.cond, name) || exprRefersTo(e->ternary.whenTrue, name) || - exprRefersTo(e->ternary.whenFalse, name); - case ExprKind::Comma: - return exprRefersTo(e->comma.lhs, name) || exprRefersTo(e->comma.rhs, name); - case ExprKind::Cast: - return exprRefersTo(e->cast.operand, name); - case ExprKind::Sizeof: - return exprRefersTo(e->sizeOf.operand, name); - case ExprKind::Member: - return exprRefersTo(e->member.base, name); - case ExprKind::VaArg: - return exprRefersTo(e->vaArg.ap, name); - case ExprKind::CompoundLit: - if(exprRefersTo(e->compound.init, name)) - return true; - break; - case ExprKind::Call: - if(exprRefersTo(e->call.target, name)) - return true; - break; - default: - break; - } - for(const Expr* a : e->args) - if(exprRefersTo(a, name)) - return true; - return false; - } - - void Emitter::collectSwitchCases(const Stmt* s, List& cases, const Stmt*& def) { - if(!s) - return; - switch(s->kind) { - case StmtKind::Case: - cases.push_back(s); - collectSwitchCases(s->thenBody, cases, def); - return; - case StmtKind::Default: - def = s; - collectSwitchCases(s->thenBody, cases, def); - return; - case StmtKind::Switch: - return; - case StmtKind::Compound: - for(const Stmt* c : s->body) - collectSwitchCases(c, cases, def); - return; - case StmtKind::If: - collectSwitchCases(s->thenBody, cases, def); - collectSwitchCases(s->elseBody, cases, def); - return; - case StmtKind::While: - case StmtKind::DoWhile: - case StmtKind::For: - case StmtKind::Label: - collectSwitchCases(s->thenBody, cases, def); - return; - default: - return; - } - } - B32 Emitter::emitSwitch(Function& fn, const Stmt* s) { Value ctrl = emitExpr(fn, s->expr); if(!ctrl.node) diff --git a/src/compiler/lex/lexer.cpp b/src/compiler/lex/lexer.cpp index 777b216..1a3f89e 100644 --- a/src/compiler/lex/lexer.cpp +++ b/src/compiler/lex/lexer.cpp @@ -66,348 +66,154 @@ namespace rat::cc { return true; } - B32 spellingIs(const C8* k, const C8* s, U32 n) { - U32 i = 0; - for(; i < n && k[i] && k[i] == s[i]; ++i) - ; - return i == n && k[i] == '\0'; - } - - TokKind keywordKind(const C8* s, U32 n) { - for(U32 k = (U32)TokKind::KwAuto; k <= (U32)TokKind::KwAlignas; ++k) - if(spellingIs(kTokNames[k], s, n)) - return (TokKind)k; - if(spellingIs("__typeof", s, n) || spellingIs("__typeof__", s, n)) - return TokKind::KwTypeof; - return TokKind::Identifier; - } - } // namespace detail - - Lexer::Lexer(const C8* src, U32 len) - : src(src), - len(len) {} - - void Lexer::bump() { - if(pos < len && src[pos] == '\n') { - ++line; - lineStart = pos + 1; - } - ++pos; - } - - void Lexer::skipTrivia() { - for(;;) { - C8 c = cur(); - if(c == ' ' || c == '\t' || c == '\n' || c == '\r' || c == '\f' || c == '\v') { - bump(); - } else if(c == '/' && at(pos + 1) == '/') { - bump(); - bump(); - while(pos < len && cur() != '\n') - bump(); - } else if(c == '/' && at(pos + 1) == '*') { - bump(); - bump(); - while(pos < len && !(cur() == '*' && at(pos + 1) == '/')) - bump(); - if(pos < len) { - bump(); // '*' - bump(); // '/' + // character at i, '\0' past the end + inline C8 charAt(const C8* s, U32 n, U32 i) { return i < n ? s[i] : '\0'; } + + B32 scanSuffix(const C8* s, U32 n, U32& i, B32 isFloat, TokKind& kind, String& err) { + U32 start = i; + while(isIdentCont(charAt(s, n, i))) + ++i; + if(isFloat) { + if(!validFloatSuffix(s + start, i - start)) { + err = "invalid suffix on floating constant"; + return false; } - } else { - return; + kind = TokKind::FloatConstant; + return true; } - } - } - - Token Lexer::finish(Token tok, TokKind kind) { - tok.kind = kind; - tok.length = pos - tok.offset; - return tok; - } - - Token Lexer::fail(Token tok, const String& msg) { - errMsg = msg; - if(pos == tok.offset && pos < len) - bump(); // always make progress - tok.kind = TokKind::Error; - tok.length = pos - tok.offset; - return tok; - } - - Token Lexer::next() { - skipTrivia(); - - Token tok; - tok.offset = pos; - tok.line = line; - tok.col = pos - lineStart + 1; - - if(pos >= len) - return finish(tok, TokKind::Eof); - - C8 c = cur(); - if(c == 'L' || c == 'u' || c == 'U') { - C8 n1 = at(pos + 1); - if(c == 'u' && n1 == '8' && at(pos + 2) == '"') { - bump(); // 'u' - bump(); // '8' - return lexString(tok); - } - if(n1 == '\'') { - bump(); // prefix - return lexChar(tok); - } - if(n1 == '"') { - bump(); // prefix - return lexString(tok); - } - } - if(isIdentStart(c) || isUcnStart(pos)) - return lexIdentifier(tok); - if(isDigit(c) || (c == '.' && isDigit(at(pos + 1)))) - return lexNumber(tok); - if(c == '\'') - return lexChar(tok); - if(c == '"') - return lexString(tok); - return lexPunct(tok); - } - - B32 Lexer::isUcnStart(U32 p) const { - if(at(p) != '\\') - return false; - C8 n = at(p + 1); - return n == 'u' || n == 'U'; - } - - Token Lexer::lexIdentifier(Token tok) { - for(;;) { - if(isIdentCont(cur())) { - bump(); - } else if(isUcnStart(pos)) { - bump(); // backslash - C8 kind = cur(); - bump(); // 'u' or 'U' - U32 ndigits = (kind == 'u') ? 4 : 8; - for(U32 k = 0; k < ndigits; ++k) { - if(!isHexDigit(cur())) - return fail(tok, "incomplete universal character name"); - bump(); - } - } else { - break; + if(!validIntSuffix(s + start, i - start)) { + err = "invalid suffix on integer constant"; + return false; } + kind = TokKind::IntConstant; + return true; } - U32 n = pos - tok.offset; - return finish(tok, detail::keywordKind(src + tok.offset, n)); - } - Token Lexer::lexNumber(Token tok) { - if(cur() == '0' && (at(pos + 1) == 'x' || at(pos + 1) == 'X')) { - bump(); - bump(); + B32 scanHexNumber(const C8* s, U32 n, U32& i, B32& isFloat, String& err) { + i += 2; // "0x" B32 anyDigits = false; - while(isHexDigit(cur())) { + while(isHexDigit(charAt(s, n, i))) { anyDigits = true; - bump(); + ++i; } - B32 isFloat = false; - if(cur() == '.') { + if(charAt(s, n, i) == '.') { isFloat = true; - bump(); - while(isHexDigit(cur())) { + ++i; + while(isHexDigit(charAt(s, n, i))) { anyDigits = true; - bump(); + ++i; } } - if(!anyDigits) - return fail(tok, "expected hex digits after '0x'"); - if(cur() == 'p' || cur() == 'P') { - isFloat = true; - bump(); - if(cur() == '+' || cur() == '-') - bump(); - if(!isDigit(cur())) - return fail(tok, "expected digits in binary exponent"); - while(isDigit(cur())) - bump(); - } else if(isFloat) { - return fail(tok, "hexadecimal floating constant requires an exponent"); + if(!anyDigits) { + err = "expected hex digits after '0x'"; + return false; + } + C8 c = charAt(s, n, i); + if(c != 'p' && c != 'P') { + if(!isFloat) + return true; + err = "hexadecimal floating constant requires an exponent"; + return false; } - if(isFloat) - return lexFloatSuffix(tok); - return lexIntSuffix(tok); - } - - B32 isFloat = false; - while(isDigit(cur())) - bump(); - if(cur() == '.') { isFloat = true; - bump(); - while(isDigit(cur())) - bump(); + ++i; + if(charAt(s, n, i) == '+' || charAt(s, n, i) == '-') + ++i; + if(!isDigit(charAt(s, n, i))) { + err = "expected digits in binary exponent"; + return false; + } + while(isDigit(charAt(s, n, i))) + ++i; + return true; } - if(cur() == 'e' || cur() == 'E') { + + B32 scanDecNumber(const C8* s, U32 n, U32& i, B32& isFloat, String& err) { + while(isDigit(charAt(s, n, i))) + ++i; + if(charAt(s, n, i) == '.') { + isFloat = true; + ++i; + while(isDigit(charAt(s, n, i))) + ++i; + } + C8 c = charAt(s, n, i); + if(c != 'e' && c != 'E') + return true; isFloat = true; - bump(); - if(cur() == '+' || cur() == '-') - bump(); - if(!isDigit(cur())) - return fail(tok, "expected digits in exponent"); - while(isDigit(cur())) - bump(); + ++i; + if(charAt(s, n, i) == '+' || charAt(s, n, i) == '-') + ++i; + if(!isDigit(charAt(s, n, i))) { + err = "expected digits in exponent"; + return false; + } + while(isDigit(charAt(s, n, i))) + ++i; + return true; } - if(isFloat) - return lexFloatSuffix(tok); - return lexIntSuffix(tok); - } - - Token Lexer::lexIntSuffix(Token tok) { - U32 sfxStart = pos; - while(isIdentCont(cur())) - bump(); - if(!detail::validIntSuffix(src + sfxStart, pos - sfxStart)) - return fail(tok, "invalid suffix on integer constant"); - return finish(tok, TokKind::IntConstant); - } - - Token Lexer::lexFloatSuffix(Token tok) { - U32 sfxStart = pos; - while(isIdentCont(cur())) - bump(); - if(!detail::validFloatSuffix(src + sfxStart, pos - sfxStart)) - return fail(tok, "invalid suffix on floating constant"); - return finish(tok, TokKind::FloatConstant); - } - - Token Lexer::lexQuoted(Token tok, C8 quote, const C8* unterminated, TokKind kind) { - bump(); // opening quote - while(pos < len && cur() != quote && cur() != '\n') { - if(cur() == '\\') - bump(); // consume the backslash - bump(); + B32 scanNumber(const C8* s, U32 n, U32& i, TokKind& kind, String& err) { + B32 isFloat = false; + C8 c1 = charAt(s, n, i + 1); + B32 hex = charAt(s, n, i) == '0' && (c1 == 'x' || c1 == 'X'); + B32 ok = hex ? scanHexNumber(s, n, i, isFloat, err) : scanDecNumber(s, n, i, isFloat, err); + if(!ok) + return false; + return scanSuffix(s, n, i, isFloat, kind, err); } - if(cur() != quote) - return fail(tok, unterminated); - bump(); // closing quote - return finish(tok, kind); - } - Token Lexer::lexChar(Token tok) { - return lexQuoted(tok, '\'', "unterminated character constant", TokKind::CharConstant); - } - - Token Lexer::lexString(Token tok) { - return lexQuoted(tok, '"', "unterminated string literal", TokKind::StringLiteral); - } - - Token Lexer::lexAltOp(Token tok, TokKind base, std::initializer_list alts) { - bump(); - for(const PunctAlt& a : alts) - if(cur() == a.c) { - bump(); - return finish(tok, a.kind); + B32 scanQuoted(const C8* s, U32 n, U32& i, C8 quote, const C8* unterminated, String& err) { + ++i; // opening quote + while(i < n && s[i] != quote && s[i] != '\n') { + if(s[i] == '\\') + ++i; // an escaped character never closes the literal + ++i; } - return finish(tok, base); - } - - Token Lexer::lexPunct(Token tok) { - C8 c = cur(); - - struct Simple { - C8 c; - TokKind kind; - }; - static const Simple kSimple[] = { - {'(', TokKind::LParen}, - {')', TokKind::RParen}, - {'{', TokKind::LBrace}, - {'}', TokKind::RBrace}, - {'[', TokKind::LBracket}, - {']', TokKind::RBracket}, - {';', TokKind::Semicolon}, - {',', TokKind::Comma}, - {'~', TokKind::Tilde}, - {'?', TokKind::Question}, - {':', TokKind::Colon}, - }; - for(const Simple& s : kSimple) - if(s.c == c) { - bump(); - return finish(tok, s.kind); + if(charAt(s, n, i) != quote) { + err = unterminated; + return false; } + ++i; // closing quote + return true; + } - switch(c) { - case '.': - if(at(pos + 1) == '.' && at(pos + 2) == '.') { - bump(); - bump(); - bump(); - return finish(tok, TokKind::Ellipsis); - } - bump(); - return finish(tok, TokKind::Dot); - case '+': - return lexAltOp(tok, TokKind::Plus, {{'+', TokKind::PlusPlus}, {'=', TokKind::PlusEq}}); - case '-': - return lexAltOp(tok, - TokKind::Minus, - {{'-', TokKind::MinusMinus}, {'=', TokKind::MinusEq}, {'>', TokKind::Arrow}}); - case '*': - return lexAltOp(tok, TokKind::Star, {{'=', TokKind::StarEq}}); - case '/': - return lexAltOp(tok, TokKind::Slash, {{'=', TokKind::SlashEq}}); - case '%': - return lexAltOp(tok, TokKind::Percent, {{'=', TokKind::PercentEq}}); - case '^': - return lexAltOp(tok, TokKind::Caret, {{'=', TokKind::CaretEq}}); - case '!': - return lexAltOp(tok, TokKind::Bang, {{'=', TokKind::BangEq}}); - case '=': - return lexAltOp(tok, TokKind::Assign, {{'=', TokKind::EqEq}}); - case '&': - return lexAltOp(tok, TokKind::Amp, {{'&', TokKind::AmpAmp}, {'=', TokKind::AmpEq}}); - case '|': - return lexAltOp(tok, TokKind::Pipe, {{'|', TokKind::PipePipe}, {'=', TokKind::PipeEq}}); - case '<': - bump(); - if(cur() == '<') { - bump(); - if(cur() == '=') { - bump(); - return finish(tok, TokKind::ShlEq); - } - return finish(tok, TokKind::Shl); - } - if(cur() == '=') { - bump(); - return finish(tok, TokKind::Le); - } - return finish(tok, TokKind::Lt); - case '>': - bump(); - if(cur() == '>') { - bump(); - if(cur() == '=') { - bump(); - return finish(tok, TokKind::ShrEq); - } - return finish(tok, TokKind::Shr); + // length of an encoding prefix: L/u/U before a quote, u8 before '"' + U32 encodingPrefix(const C8* s, U32 n) { + C8 c = charAt(s, n, 0), c1 = charAt(s, n, 1); + if(c != 'L' && c != 'u' && c != 'U') + return 0; + if(c == 'u' && c1 == '8' && charAt(s, n, 2) == '"') + return 2; + return (c1 == '\'' || c1 == '"') ? 1 : 0; + } + + TokKind classifyLiteral(const String& text, String& err) { + const C8* s = text.data(); + U32 n = (U32)text.size(); + U32 i = encodingPrefix(s, n); + TokKind kind = TokKind::Error; + B32 ok = false; + C8 c = charAt(s, n, i); + if(c == '\'') { + ok = scanQuoted(s, n, i, '\'', "unterminated character constant", err); + kind = TokKind::CharConstant; + } else if(c == '"') { + ok = scanQuoted(s, n, i, '"', "unterminated string literal", err); + kind = TokKind::StringLiteral; + } else { + i = 0; // no prefix on a pp-number + ok = scanNumber(s, n, i, kind, err); } - if(cur() == '=') { - bump(); - return finish(tok, TokKind::Ge); + if(!ok) + return TokKind::Error; + if(i != n) { + err = "malformed token '" + text + "'"; + return TokKind::Error; } - return finish(tok, TokKind::Gt); - default: - return fail(tok, String("unexpected character '") + c + "'"); + return kind; } - } - - String Lexer::text(const Token& tok) const { return String(src + tok.offset, tok.length); } + } // namespace detail const C8* tokKindName(TokKind kind) { return detail::kTokNames[(U32)kind]; } } // namespace rat::cc diff --git a/src/compiler/lex/lexer.h b/src/compiler/lex/lexer.h index 26d0050..1878294 100644 --- a/src/compiler/lex/lexer.h +++ b/src/compiler/lex/lexer.h @@ -115,8 +115,7 @@ namespace rat::cc { struct Token { TokKind kind = TokKind::Eof; - U32 offset = 0; // byte offset of the lexeme in the source buffer - U32 length = 0; // lexeme length in bytes + U32 offset = 0; // index of the token in its stream U32 line = 1; // 1-based line of the first character U32 col = 1; // 1-based column of the first character }; @@ -124,51 +123,18 @@ namespace rat::cc { namespace detail { B32 validIntSuffix(const C8* s, U32 n); B32 validFloatSuffix(const C8* s, U32 n); - B32 spellingIs(const C8* k, const C8* s, U32 n); - TokKind keywordKind(const C8* s, U32 n); - } // namespace detail - - struct Lexer { - Lexer(const C8* src, U32 len); - - Token next(); - - String text(const Token& tok) const; - - const String& error() const { return errMsg; } - private: - void skipTrivia(); - void bump(); - Token lexIdentifier(Token tok); - Token lexNumber(Token tok); - Token lexIntSuffix(Token tok); - Token lexFloatSuffix(Token tok); - Token lexChar(Token tok); - Token lexString(Token tok); - Token lexQuoted(Token tok, C8 quote, const C8* unterminated, TokKind kind); - struct PunctAlt { - C8 c; - TokKind kind; - }; - Token lexPunct(Token tok); - Token lexAltOp(Token tok, TokKind base, std::initializer_list alts); + // scanners over one token spelling; advance i, false on error with err set + B32 scanSuffix(const C8* s, U32 n, U32& i, B32 isFloat, TokKind& kind, String& err); + B32 scanHexNumber(const C8* s, U32 n, U32& i, B32& isFloat, String& err); + B32 scanDecNumber(const C8* s, U32 n, U32& i, B32& isFloat, String& err); + U32 encodingPrefix(const C8* s, U32 n); + B32 scanNumber(const C8* s, U32 n, U32& i, TokKind& kind, String& err); + B32 scanQuoted(const C8* s, U32 n, U32& i, C8 quote, const C8* unterminated, String& err); - Token finish(Token tok, TokKind kind); - Token fail(Token tok, const String& msg); - - C8 at(U32 i) const { return i < len ? src[i] : '\0'; } - C8 cur() const { return at(pos); } - B32 isUcnStart(U32 p) const; - - const C8* src; - U32 len; - U32 pos = 0; - U32 line = 1; - U32 lineStart = 0; - - String errMsg; - }; + // kind of one complete pp-number, char constant or string literal + TokKind classifyLiteral(const String& text, String& err); + } // namespace detail const C8* tokKindName(TokKind kind); } // namespace rat::cc diff --git a/src/compiler/lex/preprocess.cpp b/src/compiler/lex/preprocess.cpp index 4e6ecae..fa723d7 100644 --- a/src/compiler/lex/preprocess.cpp +++ b/src/compiler/lex/preprocess.cpp @@ -2,6 +2,7 @@ #include #include +#include #include "lex/preprocess_detail.h" diff --git a/src/compiler/lex/preprocess_lex.cpp b/src/compiler/lex/preprocess_lex.cpp index c2cc5c7..9ffea8c 100644 --- a/src/compiler/lex/preprocess_lex.cpp +++ b/src/compiler/lex/preprocess_lex.cpp @@ -2,6 +2,8 @@ #include "lex/char_class.h" +#include + namespace rat::cc { namespace detail { String unquote(const String& s) { return s.size() >= 2 ? s.substr(1, s.size() - 2) : s; } diff --git a/src/compiler/lex/preprocess_macro.cpp b/src/compiler/lex/preprocess_macro.cpp index b317437..99a6b77 100644 --- a/src/compiler/lex/preprocess_macro.cpp +++ b/src/compiler/lex/preprocess_macro.cpp @@ -2,6 +2,8 @@ #include +#include "hash.h" + namespace rat::cc { namespace detail { // names arrive sorted+unique (kept by every ctor below), so interning @@ -9,11 +11,9 @@ namespace rat::cc { const HideSet* Preprocessor::internHide(List names) { if(names.empty()) return nullptr; - U64 h = 1469598103934665603ull; - for(const String* p : names) { - h ^= (U64)(uintptr_t)p; - h *= 1099511628211ull; - } + U64 h = kFnvBasis; + for(const String* p : names) + hashMix(h, (U64)(uintptr_t)p); List& bucket = hidePool[h]; for(const HideSet* c : bucket) if(c->names == names) diff --git a/src/compiler/lex/token_stream.cpp b/src/compiler/lex/token_stream.cpp index 523848c..6b6e84d 100644 --- a/src/compiler/lex/token_stream.cpp +++ b/src/compiler/lex/token_stream.cpp @@ -3,27 +3,9 @@ #include "lex/preprocess_detail.h" namespace rat::cc { - namespace detail { - // kTokNames doubles as the keyword/punct spelling table - static_assert((U32)TokKind::KwAlignas + 1 == (U32)TokKind::LParen, - "keywords and punctuators must be contiguous"); - - // classify a pp-number/literal by lexing its text (suffix validation) - TokKind classifySingle(const String& text, String& err) { - Lexer lx(text.data(), (U32)text.size()); - Token t = lx.next(); - if(t.kind == TokKind::Error) { - err = lx.error(); - return TokKind::Error; - } - if((U64)t.length != text.size()) { - err = "malformed token '" + text + "'"; - return TokKind::Error; - } - return t.kind; - } - } // namespace detail - using namespace detail; + // kTokNames doubles as the keyword/punct spelling table + static_assert((U32)TokKind::KwAlignas + 1 == (U32)TokKind::LParen, + "keywords and punctuators must be contiguous"); B32 preprocessToTokens(const String& path, const String& source, @@ -66,12 +48,12 @@ namespace rat::cc { for(const detail::PpToken& t : pp.out) { switch(t.kind) { - case Pk::Id: { + case detail::Pk::Id: { auto it = kindOf.find(t.text); push(it != kindOf.end() ? it->second : TokKind::Identifier, t.text, t.line); break; } - case Pk::Punct: { + case detail::Pk::Punct: { auto it = kindOf.find(t.text); if(it != kindOf.end()) { push(it->second, t.text, t.line); @@ -84,11 +66,11 @@ namespace rat::cc { } break; } - case Pk::Num: - case Pk::Char: - case Pk::Str: { + case detail::Pk::Num: + case detail::Pk::Char: + case detail::Pk::Str: { String lerr; - TokKind k = detail::classifySingle(*t.text, lerr); + TokKind k = detail::classifyLiteral(*t.text, lerr); if(k == TokKind::Error && !sawError) { sawError = true; ts.errMsg = lerr; diff --git a/src/compiler/lex/token_stream.h b/src/compiler/lex/token_stream.h index f2a33c4..13ef71c 100644 --- a/src/compiler/lex/token_stream.h +++ b/src/compiler/lex/token_stream.h @@ -7,12 +7,8 @@ #include "lex/preprocess.h" namespace rat::cc { - namespace detail { - TokKind classifySingle(const String& text, String& err); - } // namespace detail - - // parser tokens straight from the pp, no serialize-then-relex; drop-in for - // Lexer: Token.offset indexes the stream, text() gives the interned spelling + // parser tokens straight from the pp, no serialize-then-relex: + // Token.offset indexes the stream, text() gives the interned spelling struct TokenStream { List toks; // always ends with Eof List texts; // parallel to toks diff --git a/src/compiler/main.cpp b/src/compiler/main.cpp index b8c0713..10c8704 100644 --- a/src/compiler/main.cpp +++ b/src/compiler/main.cpp @@ -8,6 +8,8 @@ #include "lex/preprocess.h" #include "parse/parser.h" #include +#include +#include #ifdef __GLIBC__ #include #endif @@ -155,17 +157,17 @@ namespace detail { return opt; } - I32 emitTokens(const String& source, std::ostream& os) { - Lexer lex(source.data(), (U32)source.size()); - for(;;) { - Token tok = lex.next(); - os << tok.line << ":" << tok.col << "\t" << tokKindName(tok.kind); + // dump the pp token stream the parser sees: source line, kind, spelling + I32 emitTokens(const TokenStream& ts, std::ostream& os) { + for(const Token& tok : ts.toks) { + os << tok.line << "\t" << tokKindName(tok.kind); if(tok.kind == TokKind::Error) - return os << "\t" << lex.error() << "\n", 1; + return os << "\t" << ts.error() << "\n", 1; if(tok.kind == TokKind::Eof) return os << "\n", 0; - os << "\t'" << lex.text(tok) << "'\n"; + os << "\t'" << ts.text(tok) << "'\n"; } + return 0; } TransUnit* parse(TokenStream& ts, Arena& arena) { @@ -226,12 +228,12 @@ namespace detail { return 0; } - I32 emitOne(const Options& opt, const String& pped, TokenStream* ts, Emit kind) { + I32 emitOne(const Options& opt, TokenStream* ts, Emit kind) { std::ofstream file; if(!cli::openOutput(kTool, pathFor(opt, kind), file, kind == Emit::X86)) return 1; if(kind == Emit::Tok) - return emitTokens(pped, file); + return emitTokens(*ts, file); if(kind == Emit::Ast) return emitAstText(*ts, file); return emitViaModule(opt, *ts, file); @@ -261,13 +263,8 @@ static I32 run(I32 argc, C8** argv) { if(!opt.noPredefs) source = builtinPredefs(hostTargetTriple()) + "#line 1 \"" + path + "\"\n" + source; - // -E and -emit tok need serialized text; else parse the pp token stream directly - B32 needText = opt.preprocessOnly, needToks = false; - for(::detail::Emit kind : opt.emits) - if(kind == ::detail::Emit::Tok) - needText = true; - else - needToks = true; + // only -E needs serialized text; every -emit kind runs off the pp token stream + B32 needText = opt.preprocessOnly, needToks = !opt.emits.empty(); String pped, ppErr; TokenStream ts; @@ -281,7 +278,7 @@ static I32 run(I32 argc, C8** argv) { return std::cout << pped, 0; for(::detail::Emit kind : opt.emits) - if(I32 rc = ::detail::emitOne(opt, pped, needToks ? &ts : nullptr, kind)) + if(I32 rc = ::detail::emitOne(opt, &ts, kind)) return rc; return 0; } diff --git a/src/compiler/parse/ast.cpp b/src/compiler/parse/ast.cpp index 4c1b6af..ef89751 100644 --- a/src/compiler/parse/ast.cpp +++ b/src/compiler/parse/ast.cpp @@ -1,5 +1,7 @@ #include "parse/ast.h" +#include + namespace rat::cc { StructType* makeComplexLayout(Arena& arena, CType complexType) { U32 elemBytes = (complexType.bits + 7) / 8; diff --git a/src/compiler/parse/ast.h b/src/compiler/parse/ast.h index b11cade..3abd8c3 100644 --- a/src/compiler/parse/ast.h +++ b/src/compiler/parse/ast.h @@ -3,6 +3,8 @@ #include "core.h" +#include + namespace rat::cc { enum class ExprOp : U8 { // unary diff --git a/src/compiler/parse/parse_primary.cpp b/src/compiler/parse/parse_primary.cpp index 5431e9c..38f74f6 100644 --- a/src/compiler/parse/parse_primary.cpp +++ b/src/compiler/parse/parse_primary.cpp @@ -3,6 +3,7 @@ #include "parse/parser_detail.h" #include +#include namespace rat::cc { Expr* Parser::parseBuiltinOffsetof(const Token& kw) { diff --git a/src/compiler/parse/parser.cpp b/src/compiler/parse/parser.cpp index 98df1b3..930adfc 100644 --- a/src/compiler/parse/parser.cpp +++ b/src/compiler/parse/parser.cpp @@ -1,5 +1,7 @@ #include "parse/parser.h" +#include + namespace rat::cc { B32 Parser::enterDepth() { if(++parseDepth > kMaxParseDepth) { diff --git a/src/compiler/test/correctness/custom/bool_cast_const.c b/src/compiler/test/correctness/custom/bool_cast_const.c new file mode 100644 index 0000000..9f58229 --- /dev/null +++ b/src/compiler/test/correctness/custom/bool_cast_const.c @@ -0,0 +1,16 @@ +// expect: 0 +// a cast to _Bool yields 0 or 1, never the low bit of the value (C11 6.3.1.2). +// the constant fold used to mask to bit 0, so (_Bool)2 became 0. +static const int s2 = (_Bool)2; +static const int s3 = (_Bool)3; +static const int s256 = (_Bool)256; +enum { E2 = (_Bool)2 }; + +int main(void) { + _Bool r = (_Bool)2; + if(s2 != 1 || s3 != 1 || s256 != 1) + return 1; + if(E2 != 1 || (int)r != 1) + return 2; + return 0; +} diff --git a/src/compiler/test/correctness/custom/wide_string_global_init.c b/src/compiler/test/correctness/custom/wide_string_global_init.c new file mode 100644 index 0000000..dbcc90c --- /dev/null +++ b/src/compiler/test/correctness/custom/wide_string_global_init.c @@ -0,0 +1,19 @@ +// expect: 0 +// a wide string initializer works at file scope, not only inside a function. +// file scope used to demand an 8-bit element type and reject u"..." outright. +unsigned short g[] = u"hi"; +unsigned int gl[] = U"hi"; +char gc[] = "hi"; + +int main(void) { + unsigned short l[] = u"hi"; + if(sizeof g != 6 || g[0] != 104 || g[1] != 105 || g[2] != 0) + return 1; + if(sizeof gl != 12 || gl[0] != 104 || gl[2] != 0) + return 2; + if(sizeof gc != 3 || gc[0] != 104) + return 3; + if(sizeof l != sizeof g || l[0] != g[0] || l[1] != g[1]) + return 4; + return 0; +} diff --git a/src/linker/elf_read.cpp b/src/linker/elf_read.cpp index 89b848f..d096027 100644 --- a/src/linker/elf_read.cpp +++ b/src/linker/elf_read.cpp @@ -1,6 +1,6 @@ #include "elf_read.h" -#include +#include #include #include diff --git a/src/linker/main.cpp b/src/linker/main.cpp index 373abc2..2770489 100644 --- a/src/linker/main.cpp +++ b/src/linker/main.cpp @@ -3,6 +3,8 @@ #include "cli.h" #include "string.h" +#include + using namespace rat; namespace detail {