compiler/
radiance/
radiance.rad
53.7 KiB
lib/
scripts/
seed/
sublime/
test/
vim/
.gitignore
336 B
.gitsigners
112 B
CELL_PERMISSIONS
6.8 KiB
CONTRIBUTING
2.1 KiB
LICENSE
1.1 KiB
Makefile
5.4 KiB
README
2.5 KiB
STYLE
2.5 KiB
std.lib
1.5 KiB
std.lib.test
808 B
compiler/radiance.rad
raw
| 1 | //! Radiance compiler front-end. |
| 2 | mod codegen; |
| 3 | |
| 4 | use std::mem; |
| 5 | use std::fmt; |
| 6 | use std::io; |
| 7 | use std::lang::alloc; |
| 8 | use std::lang::ast; |
| 9 | use std::lang::parser; |
| 10 | use std::lang::scanner; |
| 11 | use std::lang::resolver; |
| 12 | use std::lang::module; |
| 13 | use std::lang::strings; |
| 14 | use std::lang::package; |
| 15 | use std::lang::il; |
| 16 | use std::lang::lower; |
| 17 | use std::arch::rv64; |
| 18 | use std::arch::rv64::asm; |
| 19 | use std::arch::rv64::emit; |
| 20 | use std::arch::rv64::printer; |
| 21 | use std::lang::sexpr; |
| 22 | use std::lang::gen::data; |
| 23 | use std::lang::il::binary; |
| 24 | use std::lang::il::binary::collect; |
| 25 | use std::lang::il::binary::program; |
| 26 | use std::lang::gen::labels; |
| 27 | use std::lang::gen::types; |
| 28 | use std::sys; |
| 29 | use std::sys::unix; |
| 30 | use std::collections::dict; |
| 31 | |
| 32 | /// Maximum number of modules we can load per package. |
| 33 | constant MAX_LOADED_MODULES: u32 = 128; |
| 34 | /// Maximum number of packages we can compile. |
| 35 | constant MAX_PACKAGES: u32 = 4; |
| 36 | /// Source code buffer arena (4 MB). |
| 37 | constant MAX_SOURCES_SIZE: u32 = 4194304; |
| 38 | /// Maximum number of test functions we can discover. |
| 39 | constant MAX_TESTS: u32 = 1024; |
| 40 | /// Maximum number of assembly source paths we can load per package. |
| 41 | constant MAX_ASM_MODULES: u32 = 64; |
| 42 | |
| 43 | /// Byte size of the five u32 fields in a single-file RV64 image header. |
| 44 | constant IMAGE_HEADER_SIZE: u32 = @sizeOf([u32; 5]); |
| 45 | |
| 46 | /// AST arena size (64 MB) - retains parsed nodes throughout compilation. |
| 47 | constant TEMP_ARENA_SIZE: u32 = 67108864; |
| 48 | /// Per-function lowering and register-allocation arena size (24 MB). |
| 49 | constant FN_ARENA_SIZE: u32 = 25165824; |
| 50 | /// Main arena size (208 MB) - lives throughout compilation. |
| 51 | /// Used for: resolver data, types, symbols, global IL data, and codegen output. |
| 52 | constant MAIN_ARENA_SIZE: u32 = 218103808; |
| 53 | |
| 54 | /// AST storage arena. |
| 55 | static TEMP_ARENA: [u8; TEMP_ARENA_SIZE] = [0; TEMP_ARENA_SIZE]; |
| 56 | /// Scratch storage reclaimed after each generated function. |
| 57 | static FN_ARENA: [u8; FN_ARENA_SIZE] = [0; FN_ARENA_SIZE]; |
| 58 | /// Main storage arena - persists throughout compilation. |
| 59 | static MAIN_ARENA: [u8; MAIN_ARENA_SIZE] = [0; MAIN_ARENA_SIZE]; |
| 60 | |
| 61 | /// Module source code. |
| 62 | static MODULE_SOURCES: [u8; MAX_SOURCES_SIZE] = [0; MAX_SOURCES_SIZE]; |
| 63 | /// Module entries for all packages. |
| 64 | unsafe static MODULE_ENTRIES: [?*module::ModuleEntry; module::MAX_MODULES] = [nil; module::MAX_MODULES]; |
| 65 | /// String pool. |
| 66 | unsafe static STRING_POOL: strings::Pool = strings::Pool { table: undefined, count: 0 }; |
| 67 | |
| 68 | /// Package scope. |
| 69 | unsafe static RESOLVER_PKG_SCOPE: resolver::Scope = undefined; |
| 70 | /// Errors emitted by resolver. |
| 71 | unsafe static RESOLVER_ERRORS: [resolver::Error; resolver::MAX_ERRORS] = undefined; |
| 72 | |
| 73 | /// Code generation storage. |
| 74 | unsafe static CODEGEN_DATA_SYMS: [data::DataSym; data::MAX_DATA_SYMS] = undefined; |
| 75 | /// Hash table entries for data symbol lookup. |
| 76 | unsafe static CODEGEN_DATA_SYM_ENTRIES: [dict::Entry; data::DATA_SYM_TABLE_SIZE] = undefined; |
| 77 | /// Emitted instruction storage. |
| 78 | static CODEGEN_INSTRUCTIONS: [u32; emit::MAX_INSTRS] = [0; emit::MAX_INSTRS]; |
| 79 | /// Local branch patch storage. |
| 80 | unsafe static CODEGEN_PENDING_BRANCHES: [emit::PendingBranch; emit::MAX_PENDING] = undefined; |
| 81 | /// Function call patch storage. |
| 82 | unsafe static CODEGEN_PENDING_CALLS: [emit::PendingCall; emit::MAX_PENDING] = undefined; |
| 83 | /// Assembly jump patch storage. |
| 84 | unsafe static CODEGEN_PENDING_JUMPS: [emit::PendingJump; emit::MAX_PENDING] = undefined; |
| 85 | /// Address load patch storage. |
| 86 | unsafe static CODEGEN_PENDING_ADDR_LOADS: [emit::PendingAddrLoad; emit::MAX_PENDING] = undefined; |
| 87 | /// Per-function block offset storage. |
| 88 | static CODEGEN_BLOCK_OFFSETS: [i32; labels::MAX_BLOCKS_PER_FN] = [0; labels::MAX_BLOCKS_PER_FN]; |
| 89 | /// Function label hash table storage. |
| 90 | unsafe static CODEGEN_FUNC_ENTRIES: [dict::Entry; labels::FUNC_TABLE_SIZE] = undefined; |
| 91 | /// Printed function address storage. |
| 92 | unsafe static CODEGEN_FUNCS: [types::FuncAddr; emit::MAX_FUNCS] = undefined; |
| 93 | /// Debug location storage. |
| 94 | unsafe static CODEGEN_DEBUG_ENTRIES: [types::DebugEntry; emit::MAX_DEBUG_ENTRIES] = undefined; |
| 95 | |
| 96 | /// Maximum number of exported definitions in one binary package. |
| 97 | constant MAX_PACKAGE_EXPORTS: u32 = 8192; |
| 98 | /// Maximum number of indexed names in one binary package. |
| 99 | constant MAX_PACKAGE_SYMBOLS: u32 = 16384; |
| 100 | /// Export workspace reused for each binary package. |
| 101 | unsafe static PACKAGE_EXPORTS: [binary::Export; MAX_PACKAGE_EXPORTS] = undefined; |
| 102 | /// Symbol workspace reused for each binary package. |
| 103 | static PACKAGE_SYMBOLS: [*[u8]; MAX_PACKAGE_SYMBOLS] = [""; MAX_PACKAGE_SYMBOLS]; |
| 104 | |
| 105 | /// Debug info file extension. |
| 106 | constant DEBUG_EXT: *[u8] = ".debug"; |
| 107 | |
| 108 | /// Maximum rodata size (4MB). |
| 109 | constant MAX_RO_DATA_SIZE: u32 = 4194304; |
| 110 | /// Maximum rwdata size (4MB). |
| 111 | constant MAX_RW_DATA_SIZE: u32 = 4194304; |
| 112 | /// Maximum path length. |
| 113 | constant MAX_PATH_LEN: u32 = 256; |
| 114 | /// Read-only data buffer. |
| 115 | static RO_DATA_BUF: [u8; MAX_RO_DATA_SIZE] = [0; MAX_RO_DATA_SIZE]; |
| 116 | /// Read-write data buffer. |
| 117 | static RW_DATA_BUF: [u8; MAX_RW_DATA_SIZE] = [0; MAX_RW_DATA_SIZE]; |
| 118 | /// Assembly module source buffer. |
| 119 | static ASM_SOURCE_BUF: [u8; MAX_SOURCES_SIZE] = [0; MAX_SOURCES_SIZE]; |
| 120 | /// Temporary assembly text buffer. |
| 121 | static ASM_TEXT_BUF: [u32; 262144] = [0; 262144]; |
| 122 | /// Temporary assembly data buffer. |
| 123 | static ASM_DATA_BUF: [u8; MAX_RO_DATA_SIZE] = [0; MAX_RO_DATA_SIZE]; |
| 124 | /// Accumulated assembly read-only data. |
| 125 | static ASM_RO_DATA_BUF: [u8; MAX_RO_DATA_SIZE] = [0; MAX_RO_DATA_SIZE]; |
| 126 | |
| 127 | /// Assembly source file extension. |
| 128 | constant ASM_SOURCE_EXT: *[u8] = ".ras"; |
| 129 | /// Symbol name exported for startup code to call the semantic entry function. |
| 130 | export constant DEFAULT_ENTRY_SYMBOL: *[u8] = "::default"; |
| 131 | |
| 132 | /// Usage string. |
| 133 | constant USAGE: *[u8] = |
| 134 | "usage: radiance -pkg <name> [-start <input.ras>] -mod <input>.. [-pkg <name> -mod <input>..] -entry <pkg> [-o <output> | -ril <directory>]\n"; |
| 135 | |
| 136 | /// Compiler error. |
| 137 | union Error: Copy { |
| 138 | Other, |
| 139 | } |
| 140 | |
| 141 | /// What to dump during compilation. |
| 142 | union Dump: Copy { |
| 143 | /// Don't dump anything. |
| 144 | None, |
| 145 | /// Dump the parsed AST before semantic analysis. |
| 146 | Ast, |
| 147 | /// Dump the module graph. |
| 148 | Graph, |
| 149 | /// Dump the IL. |
| 150 | Il, |
| 151 | /// Dump the generated assembly. |
| 152 | Asm, |
| 153 | } |
| 154 | |
| 155 | /// A discovered test function. |
| 156 | record TestDesc: Copy { |
| 157 | /// Full module qualified path segments (eg. ["std", "tests"]). |
| 158 | modPath: *[*[u8]], |
| 159 | /// Test function name (eg. "testFoo"). |
| 160 | fnName: *[u8], |
| 161 | } |
| 162 | |
| 163 | /// Source inputs belonging to one command-line package. |
| 164 | record PackageInput: Copy { |
| 165 | /// Package name from the `-pkg` argument. |
| 166 | name: *[u8], |
| 167 | /// Optional startup assembly emitted before generated text. |
| 168 | startupPath: ?*[u8], |
| 169 | /// Radiance source paths for this package. |
| 170 | radPaths: [*[u8]; MAX_LOADED_MODULES], |
| 171 | /// Number of Radiance source paths. |
| 172 | radPathCount: u32, |
| 173 | /// Assembly source paths for this package. |
| 174 | asmPaths: [*[u8]; MAX_ASM_MODULES], |
| 175 | /// Number of assembly source paths. |
| 176 | asmPathCount: u32, |
| 177 | } |
| 178 | |
| 179 | /// Validated command-line compilation options. |
| 180 | record Command { |
| 181 | /// Number of packages to compile. |
| 182 | packageCount: u32, |
| 183 | /// Index of the selected entry package. |
| 184 | entryPkgIdx: u32, |
| 185 | /// Resolver configuration. |
| 186 | config: resolver::Config, |
| 187 | /// What to dump during compilation. |
| 188 | dump: Dump, |
| 189 | /// Output path for binary. |
| 190 | outputPath: ?*[u8], |
| 191 | /// Output directory for separate binary RIL packages. |
| 192 | rilDirectory: ?*[u8], |
| 193 | /// Whether to emit debug info (.debug file). |
| 194 | debug: bool, |
| 195 | } |
| 196 | |
| 197 | /// Compilation context. |
| 198 | record CompileContext: 'permission { |
| 199 | /// Array of packages to compile. |
| 200 | packages: [package::Package; MAX_PACKAGES], |
| 201 | /// Driver inputs for each package slot. |
| 202 | inputs: [PackageInput; MAX_PACKAGES], |
| 203 | /// Number of packages. |
| 204 | packageCount: u32, |
| 205 | /// Index of entry package. |
| 206 | entryPkgIdx: ?u32, |
| 207 | /// Global module graph shared by all packages. |
| 208 | graph: module::ModuleGraph 'permission, |
| 209 | /// Resolver configuration. |
| 210 | config: resolver::Config, |
| 211 | /// What to dump during compilation. |
| 212 | dump: Dump, |
| 213 | /// Output path for binary. |
| 214 | outputPath: ?*[u8], |
| 215 | /// Output directory for separate binary RIL packages. |
| 216 | rilDirectory: ?*[u8], |
| 217 | /// Whether to emit debug info (.debug file). |
| 218 | debug: bool, |
| 219 | } |
| 220 | |
| 221 | /// Root module info for a package. |
| 222 | record RootModule: Copy { |
| 223 | entry: *module::ModuleEntry, |
| 224 | ast: *ast::Node, |
| 225 | } |
| 226 | |
| 227 | /// Entry handling for streamed code generation. |
| 228 | union CodegenEntryMode: Copy { |
| 229 | /// Do not reserve an entry jump. |
| 230 | None, |
| 231 | /// Reserve and patch an entry jump to the `@default` function. |
| 232 | DefaultEntry, |
| 233 | } |
| 234 | |
| 235 | /// Options controlling streamed lowering and code generation. |
| 236 | record CodegenOptions: Copy { |
| 237 | /// Optional output path used for progress logging. |
| 238 | logPath: ?*[u8], |
| 239 | /// Whether to emit debug source locations. |
| 240 | debug: bool, |
| 241 | /// How the generated program should handle entry. |
| 242 | entryMode: CodegenEntryMode, |
| 243 | } |
| 244 | |
| 245 | /// Print a driver error line. |
| 246 | fn error(msg: &[*[u8]]) -> Error { |
| 247 | io::printError("radiance: "); |
| 248 | |
| 249 | for part, i in msg { |
| 250 | io::printError(part); |
| 251 | if i < msg.len - 1 { |
| 252 | io::printError(" "); |
| 253 | } |
| 254 | } |
| 255 | io::printError("\n"); |
| 256 | return Error::Other; |
| 257 | } |
| 258 | |
| 259 | /// Print a log line for the given package. |
| 260 | fn pkgLog(pkg: &package::Package, msg: &[*[u8]]) { |
| 261 | io::printError("radiance: "); |
| 262 | io::printError(pkg.name); |
| 263 | io::printError(": "); |
| 264 | |
| 265 | for part, i in msg { |
| 266 | io::printError(part); |
| 267 | if i < msg.len - 1 { |
| 268 | io::printError(" "); |
| 269 | } |
| 270 | } |
| 271 | io::printError("\n"); |
| 272 | } |
| 273 | |
| 274 | /// Return `true` when `path` ends with `ext`. |
| 275 | fn hasExtension(path: *[u8], ext: *[u8]) -> bool { |
| 276 | if path.len < ext.len { |
| 277 | return false; |
| 278 | } |
| 279 | let start = path.len - ext.len; |
| 280 | return mem::eq(&path[start..], ext); |
| 281 | } |
| 282 | |
| 283 | /// Create an empty source input set for one package. |
| 284 | fn packageInput(name: *[u8]) -> PackageInput { |
| 285 | return PackageInput { |
| 286 | name, |
| 287 | startupPath: nil, |
| 288 | radPaths: [""; MAX_LOADED_MODULES], |
| 289 | radPathCount: 0, |
| 290 | asmPaths: [""; MAX_ASM_MODULES], |
| 291 | asmPathCount: 0, |
| 292 | }; |
| 293 | } |
| 294 | |
| 295 | /// Register, load, and parse `path` within `pkg`. |
| 296 | unsafe fn processModule 'permission ( |
| 297 | pkg: *unsafe mut package::Package, |
| 298 | graph: &mut module::ModuleGraph 'permission, |
| 299 | path: *[u8], |
| 300 | nodeArena: &mut ast::NodeArena, |
| 301 | sourceArena: &mut alloc::Arena |
| 302 | ) throws (Error) { |
| 303 | pkgLog(pkg, &["parsing", "(", path, ")", ".."]); |
| 304 | |
| 305 | let moduleId = try package::registerModule(pkg, graph, &mut STRING_POOL, path) catch { |
| 306 | throw error(&["error registering module"]); |
| 307 | }; |
| 308 | // Read file into remaining arena space. |
| 309 | let buffer = alloc::remainingBuf(&mut *sourceArena); |
| 310 | if buffer.len == 0 { |
| 311 | throw error(&["fatal:", "source arena exhausted"]); |
| 312 | } |
| 313 | let sourceLen = unix::readFile(path, buffer) else { |
| 314 | throw error(&["error reading file"]); |
| 315 | }; |
| 316 | let source = &buffer[..sourceLen]; |
| 317 | if source.len == buffer.len { |
| 318 | throw error(&["fatal:", "source arena too small, file truncated:", path]); |
| 319 | } |
| 320 | // Commit only what was read. |
| 321 | alloc::commit(sourceArena, source.len); |
| 322 | |
| 323 | let ast = try parser::parse(scanner::SourceLoc::File(path), source, nodeArena, &mut STRING_POOL) catch { |
| 324 | throw Error::Other; |
| 325 | }; |
| 326 | try module::setAst(graph, moduleId, ast) catch { |
| 327 | throw error(&["error setting AST"]); |
| 328 | }; |
| 329 | try module::setSource(graph, moduleId, source) catch { |
| 330 | throw error(&["error setting source"]); |
| 331 | }; |
| 332 | } |
| 333 | |
| 334 | /// Consume the next argument, or print an error and throw. |
| 335 | fn nextArg(args: *[*[u8]], idx: &mut u32, msg: &[*[u8]]) -> *[u8] throws (Error) { |
| 336 | set *idx += 1; |
| 337 | if *idx >= args.len { |
| 338 | throw error(msg); |
| 339 | } |
| 340 | return args[*idx]; |
| 341 | } |
| 342 | |
| 343 | /// Parse and validate command-line inputs and compilation options. |
| 344 | fn parseCommand( |
| 345 | args: *[*[u8]], |
| 346 | inputs: &mut [PackageInput] |
| 347 | ) -> Command throws (Error) { |
| 348 | let mut buildTest = false; |
| 349 | let mut debugEnabled = false; |
| 350 | let mut outputPath: ?*[u8] = nil; |
| 351 | let mut rilDirectory: ?*[u8] = nil; |
| 352 | let mut dump = Dump::None; |
| 353 | let mut entryPkgName: ?*[u8] = nil; |
| 354 | |
| 355 | // Per-package source path tracking. |
| 356 | let mut pkgCount: u32 = 0; |
| 357 | let mut currentPkgIdx: ?u32 = nil; |
| 358 | |
| 359 | if args.len == 0 { |
| 360 | io::printError(USAGE); |
| 361 | throw Error::Other; |
| 362 | } |
| 363 | let mut idx: u32 = 0; |
| 364 | |
| 365 | while idx < args.len { |
| 366 | let arg = args[idx]; |
| 367 | if mem::eq(arg, "-pkg") { |
| 368 | try nextArg(args, &mut idx, &["`-pkg` requires a package name"]); |
| 369 | if pkgCount >= inputs.len { |
| 370 | throw error(&["too many packages specified"]); |
| 371 | } |
| 372 | set inputs[pkgCount] = packageInput(args[idx]); |
| 373 | set currentPkgIdx = pkgCount; |
| 374 | set pkgCount += 1; |
| 375 | } else if mem::eq(arg, "-mod") { |
| 376 | try nextArg(args, &mut idx, &["`-mod` requires a module path"]); |
| 377 | let pkgIdx = currentPkgIdx else { |
| 378 | throw error(&["`-mod` must follow a `-pkg` argument"]); |
| 379 | }; |
| 380 | let input = &mut inputs[pkgIdx]; |
| 381 | if hasExtension(args[idx], ASM_SOURCE_EXT) { |
| 382 | if input.asmPathCount >= MAX_ASM_MODULES { |
| 383 | throw error(&["too many assembly modules specified"]); |
| 384 | } |
| 385 | set input.asmPaths[input.asmPathCount] = args[idx]; |
| 386 | set input.asmPathCount += 1; |
| 387 | } else { |
| 388 | if input.radPathCount >= MAX_LOADED_MODULES { |
| 389 | throw error(&["too many modules specified for package"]); |
| 390 | } |
| 391 | set input.radPaths[input.radPathCount] = args[idx]; |
| 392 | set input.radPathCount += 1; |
| 393 | } |
| 394 | } else if mem::eq(arg, "-start") { |
| 395 | try nextArg(args, &mut idx, &["`-start` requires an assembly path"]); |
| 396 | let pkgIdx = currentPkgIdx else { |
| 397 | throw error(&["`-start` must follow a `-pkg` argument"]); |
| 398 | }; |
| 399 | let input = &mut inputs[pkgIdx]; |
| 400 | if input.startupPath <> nil { |
| 401 | throw error(&["package", input.name, "has more than one startup file"]); |
| 402 | } |
| 403 | if not hasExtension(args[idx], ASM_SOURCE_EXT) { |
| 404 | throw error(&["`-start` requires a `.ras` assembly file"]); |
| 405 | } |
| 406 | set input.startupPath = args[idx]; |
| 407 | } else if mem::eq(arg, "-entry") { |
| 408 | try nextArg(args, &mut idx, &["`-entry` requires a package name"]); |
| 409 | set entryPkgName = args[idx]; |
| 410 | } else if mem::eq(arg, "-test") { |
| 411 | set buildTest = true; |
| 412 | } else if mem::eq(arg, "-debug") { |
| 413 | set debugEnabled = true; |
| 414 | } else if mem::eq(arg, "-o") { |
| 415 | try nextArg(args, &mut idx, &["`-o` requires an output path"]); |
| 416 | set outputPath = args[idx]; |
| 417 | } else if mem::eq(arg, "-ril") { |
| 418 | try nextArg(args, &mut idx, &["`-ril` requires an output directory"]); |
| 419 | set rilDirectory = args[idx]; |
| 420 | } else if mem::eq(arg, "-dump") { |
| 421 | try nextArg(args, &mut idx, &["`-dump` requires a mode (eg. ast)"]); |
| 422 | let mode = args[idx]; |
| 423 | if mem::eq(mode, "ast") { |
| 424 | set dump = Dump::Ast; |
| 425 | } else if mem::eq(mode, "graph") { |
| 426 | set dump = Dump::Graph; |
| 427 | } else if mem::eq(mode, "il") { |
| 428 | set dump = Dump::Il; |
| 429 | } else if mem::eq(mode, "asm") { |
| 430 | set dump = Dump::Asm; |
| 431 | } else { |
| 432 | throw error(&["unknown dump mode", mode, "(expected: ast, graph, il, asm)"]); |
| 433 | } |
| 434 | } else { |
| 435 | throw error(&["unknown argument", arg]); |
| 436 | } |
| 437 | set idx += 1; |
| 438 | } |
| 439 | if pkgCount == 0 { |
| 440 | throw error(&["no package specified"]); |
| 441 | } |
| 442 | for i in 0..pkgCount { |
| 443 | if inputs[i].radPathCount == 0 { |
| 444 | throw error(&["package", inputs[i].name, "has no Radiance modules specified"]); |
| 445 | } |
| 446 | } |
| 447 | |
| 448 | // Determine entry package index. |
| 449 | let mut entryPkgIdx: ?u32 = nil; |
| 450 | if pkgCount == 1 { |
| 451 | // Single package: it is the entry. |
| 452 | set entryPkgIdx = 0; |
| 453 | } else { |
| 454 | // Multiple packages: need -entry. |
| 455 | let entryName = entryPkgName else { |
| 456 | throw error(&["`-entry` required when multiple packages specified"]); |
| 457 | }; |
| 458 | for i in 0..pkgCount { |
| 459 | if mem::eq(inputs[i].name, entryName) { |
| 460 | set entryPkgIdx = i; |
| 461 | break; |
| 462 | } |
| 463 | } |
| 464 | if entryPkgIdx == nil { |
| 465 | throw error(&["fatal:", "entry package", entryName, "not found"]); |
| 466 | } |
| 467 | } |
| 468 | let entryIdx = entryPkgIdx else { |
| 469 | panic "parseCommand: no entry package"; |
| 470 | }; |
| 471 | for i in 0..pkgCount { |
| 472 | if i <> entryIdx and inputs[i].startupPath <> nil { |
| 473 | throw error(&["`-start` is only supported on the entry package"]); |
| 474 | } |
| 475 | } |
| 476 | if rilDirectory <> nil and (outputPath <> nil or dump <> Dump::None) { |
| 477 | throw error(&["`-ril` requires a separate invocation from `-o` or `-dump`"]); |
| 478 | } |
| 479 | return Command { |
| 480 | packageCount: pkgCount, |
| 481 | entryPkgIdx: entryIdx, |
| 482 | config: resolver::Config { buildTest }, |
| 483 | dump, |
| 484 | outputPath, |
| 485 | rilDirectory, |
| 486 | debug: debugEnabled, |
| 487 | }; |
| 488 | } |
| 489 | |
| 490 | /// Parse CLI arguments and return compilation context. |
| 491 | unsafe fn processCommand 'permission ( |
| 492 | args: *[*[u8]], |
| 493 | arena: &mut ast::NodeArena, |
| 494 | permission: &'permission mut module::Permission |
| 495 | ) -> CompileContext 'permission throws (Error) { |
| 496 | let mut inputs = [packageInput(""); MAX_PACKAGES]; |
| 497 | let command = try parseCommand(args, &mut inputs[..]); |
| 498 | let graph = module::moduleGraph(&mut MODULE_ENTRIES[..], arena, permission); |
| 499 | let mut ctx = CompileContext 'permission { |
| 500 | packages: undefined, |
| 501 | inputs, |
| 502 | packageCount: command.packageCount, |
| 503 | entryPkgIdx: command.entryPkgIdx, |
| 504 | graph, |
| 505 | config: command.config, |
| 506 | dump: command.dump, |
| 507 | outputPath: command.outputPath, |
| 508 | rilDirectory: command.rilDirectory, |
| 509 | debug: command.debug, |
| 510 | }; |
| 511 | // Initialize and parse all packages. |
| 512 | let mut sourceArena = alloc::new(&mut MODULE_SOURCES[..]); |
| 513 | for i in 0..ctx.packageCount { |
| 514 | let name = ctx.inputs[i].name; |
| 515 | package::init(&mut ctx.packages[i], i as u16, name, &mut STRING_POOL); |
| 516 | |
| 517 | for j in 0..ctx.inputs[i].radPathCount { |
| 518 | let path = ctx.inputs[i].radPaths[j]; |
| 519 | try processModule(&mut ctx.packages[i], &mut ctx.graph, path, arena, &mut sourceArena); |
| 520 | } |
| 521 | } |
| 522 | return ctx; |
| 523 | } |
| 524 | |
| 525 | /// Get the entry package from the context. |
| 526 | fn getEntryPackage 'permission (ctx: &CompileContext 'permission) -> package::Package throws (Error) { |
| 527 | let entryIdx = ctx.entryPkgIdx else { |
| 528 | throw error(&["no entry package specified"]); |
| 529 | }; |
| 530 | return ctx.packages[entryIdx]; |
| 531 | } |
| 532 | |
| 533 | /// Return the startup assembly path for the entry package, if one was supplied. |
| 534 | fn getEntryStartupPath 'permission (ctx: &CompileContext 'permission) -> ?*[u8] { |
| 535 | let entryIdx = ctx.entryPkgIdx else { |
| 536 | panic "getEntryStartupPath: no entry package"; |
| 537 | }; |
| 538 | return ctx.inputs[entryIdx].startupPath; |
| 539 | } |
| 540 | |
| 541 | /// Get root module info from a package. |
| 542 | fn getRootModule 'permission (pkg: &package::Package, graph: &module::ModuleGraph 'permission) -> RootModule throws (Error) { |
| 543 | let rootId = pkg.rootModuleId else { |
| 544 | throw error(&["no root module found"]); |
| 545 | }; |
| 546 | let rootEntry = module::get(graph, rootId) else { |
| 547 | throw error(&["root module entry not found"]); |
| 548 | }; |
| 549 | let rootAst = module::astFor(graph, rootEntry) else { |
| 550 | throw error(&["root module has no AST"]); |
| 551 | }; |
| 552 | return RootModule { entry: rootEntry, ast: rootAst }; |
| 553 | } |
| 554 | |
| 555 | /// Dump the module graph. |
| 556 | unsafe fn dumpGraph 'permission (ctx: &CompileContext 'permission) { |
| 557 | let mut arena = alloc::new(&mut MAIN_ARENA[..]); |
| 558 | module::printer::printGraph(&ctx.graph, &mut arena); |
| 559 | } |
| 560 | |
| 561 | /// Dump the parsed AST. |
| 562 | unsafe fn dumpAst 'permission (ctx: &CompileContext 'permission) throws (Error) { |
| 563 | let pkg = try getEntryPackage(ctx); |
| 564 | let root = try getRootModule(&pkg, &ctx.graph); |
| 565 | let mut arena = alloc::new(&mut MAIN_ARENA[..]); |
| 566 | |
| 567 | ast::printer::printTree(root.ast, &mut arena); |
| 568 | } |
| 569 | |
| 570 | /// Lower all packages into a single IL program. |
| 571 | /// Dependencies are lowered first, then the entry package. |
| 572 | unsafe fn lowerAllPackages 'arena 'permission ( |
| 573 | ctx: &CompileContext 'permission, |
| 574 | res: *unsafe mut resolver::Resolver 'arena |
| 575 | ) -> il::Program throws (Error) { |
| 576 | let entryIdx = ctx.entryPkgIdx else { |
| 577 | panic "lowerAllPackages: no entry package"; |
| 578 | }; |
| 579 | let entryPkg = &ctx.packages[entryIdx]; |
| 580 | |
| 581 | // Create the lowerer accumulator using entry package's name. |
| 582 | let options = lower::LowerOptions { debug: ctx.debug, buildTest: ctx.config.buildTest }; |
| 583 | let arena = (&mut *res.arena) as *unsafe mut alloc::Arena; |
| 584 | let resolved: 'phase = &*res where 'arena: 'phase in { |
| 585 | let mut low = lower::lowerer(resolved, entryPkg.name, arena, options); |
| 586 | try lowerAllPackagesInto(ctx, &mut low, &mut *arena); |
| 587 | |
| 588 | // Finalize and return the unified program. |
| 589 | return lower::finalize(low); |
| 590 | } |
| 591 | } |
| 592 | |
| 593 | /// Lower all packages into an existing lowerer. |
| 594 | unsafe fn lowerAllPackagesInto 'arena 'phase 'permission ( |
| 595 | ctx: &CompileContext 'permission, |
| 596 | low: &mut lower::Lowerer 'arena 'phase, |
| 597 | functionArena: &mut alloc::Arena |
| 598 | ) throws (Error) where 'arena: 'phase { |
| 599 | let entryIdx = ctx.entryPkgIdx else { |
| 600 | panic "lowerAllPackagesInto: no entry package"; |
| 601 | }; |
| 602 | // Lower all packages except entry. |
| 603 | for i in 0..ctx.packageCount { |
| 604 | if i <> entryIdx { |
| 605 | try lowerPackage(ctx, low, &ctx.packages[i], false, functionArena); |
| 606 | } |
| 607 | } |
| 608 | // Lower entry package. |
| 609 | try lowerPackage(ctx, low, &ctx.packages[entryIdx], true, functionArena); |
| 610 | } |
| 611 | |
| 612 | /// Lower all modules in a package into the lowerer accumulator. |
| 613 | unsafe fn lowerPackage 'arena 'phase 'permission ( |
| 614 | ctx: &CompileContext 'permission, |
| 615 | low: &mut lower::Lowerer 'arena 'phase, |
| 616 | pkg: &package::Package, |
| 617 | isEntry: bool, |
| 618 | functionArena: &mut alloc::Arena |
| 619 | ) throws (Error) where 'arena: 'phase { |
| 620 | let rootId = pkg.rootModuleId else { |
| 621 | throw error(&["no root module found"]); |
| 622 | }; |
| 623 | // Set lowerer's package context for qualified name generation. |
| 624 | // TODO: We shouldn't have to call this manually. |
| 625 | lower::setPackage(low, pkg.name); |
| 626 | |
| 627 | try lowerModuleTreeInto(low, &ctx.graph, rootId, isEntry, pkg, functionArena); |
| 628 | } |
| 629 | |
| 630 | /// Recursively lower a module and all its children into the accumulator. |
| 631 | unsafe fn lowerModuleTreeInto 'arena 'phase 'permission ( |
| 632 | low: &mut lower::Lowerer 'arena 'phase, |
| 633 | graph: &module::ModuleGraph 'permission, |
| 634 | modId: u16, |
| 635 | isRoot: bool, |
| 636 | pkg: &package::Package, |
| 637 | functionArena: &mut alloc::Arena |
| 638 | ) throws (Error) where 'arena: 'phase { |
| 639 | let entry = module::get(graph, modId) else { |
| 640 | throw error(&["module entry not found"]); |
| 641 | }; |
| 642 | let modAst = module::astFor(graph, entry) else { |
| 643 | throw error(&["module has no AST"]); |
| 644 | }; |
| 645 | pkgLog(pkg, &["lowering", "(", entry.filePath, ")", ".."]); |
| 646 | |
| 647 | try lower::lowerModule(low, modId, modAst, isRoot, functionArena) catch err { |
| 648 | io::printError("radiance: "); |
| 649 | io::printError("internal error during lowering: "); |
| 650 | lower::printError(err); |
| 651 | io::printError("\n"); |
| 652 | |
| 653 | throw Error::Other; |
| 654 | }; |
| 655 | // Recurse into children. |
| 656 | for i in 0..module::childCount(graph, entry) { |
| 657 | let childId = module::childAt(graph, entry, i); |
| 658 | try lowerModuleTreeInto(low, graph, childId, false, pkg, functionArena); |
| 659 | } |
| 660 | } |
| 661 | |
| 662 | /// Build a scope access chain: a::b::c from a slice of identifiers. |
| 663 | unsafe fn synthScopeAccess(arena: &mut ast::NodeArena, path: &[*[u8]]) -> *ast::Node { |
| 664 | let mut result = ast::synthNode( |
| 665 | arena, |
| 666 | ast::NodeValue::Ident(strings::intern(&mut STRING_POOL, path[0])) |
| 667 | ); |
| 668 | for i in 1..path.len { |
| 669 | let child = ast::synthNode( |
| 670 | arena, |
| 671 | ast::NodeValue::Ident(strings::intern(&mut STRING_POOL, path[i])) |
| 672 | ); |
| 673 | set result = ast::synthNode(arena, ast::NodeValue::ScopeAccess(ast::Access { |
| 674 | parent: result, child, |
| 675 | })); |
| 676 | } |
| 677 | return result; |
| 678 | } |
| 679 | |
| 680 | /// Check if a function declaration has the `@test` attribute and return its name if so. |
| 681 | fn getTestFnName(decl: &ast::FnDecl) -> ?*[u8] { |
| 682 | let attrs = decl.attrs else { return nil; }; |
| 683 | if not ast::attributesContains(&attrs, ast::Attribute::Test) { |
| 684 | return nil; |
| 685 | } |
| 686 | let case ast::NodeValue::Ident(name) = decl.name.value |
| 687 | else return nil; |
| 688 | |
| 689 | return name; |
| 690 | } |
| 691 | |
| 692 | /// Scan a single module's AST for `@test` functions and append them to `tests`. |
| 693 | fn collectModuleTests 'permission (graph: &module::ModuleGraph 'permission, entry: *module::ModuleEntry, tests: &mut [?TestDesc], testCount: &mut u32) { |
| 694 | let modAst = module::astFor(graph, entry) else { |
| 695 | return; |
| 696 | }; |
| 697 | let case ast::NodeValue::Block(block) = modAst.value else { |
| 698 | return; |
| 699 | }; |
| 700 | let modPath = module::moduleQualifiedPath(entry); |
| 701 | |
| 702 | for stmt in block.statements { |
| 703 | if let case ast::NodeValue::FnDecl(decl) = stmt.value { |
| 704 | if let fnName = getTestFnName(&decl) { |
| 705 | if *testCount < tests.len { |
| 706 | set tests[*testCount] = TestDesc { modPath, fnName }; |
| 707 | set *testCount += 1; |
| 708 | } else { |
| 709 | panic "collectModuleTests: too many tests"; |
| 710 | } |
| 711 | } |
| 712 | } |
| 713 | } |
| 714 | } |
| 715 | |
| 716 | /// Collect initialized test descriptors from one package in module order. |
| 717 | fn collectPackageTests 'permission (graph: &module::ModuleGraph 'permission, packageId: u16, tests: &mut [?TestDesc]) -> u32 { |
| 718 | let mut count: u32 = 0; |
| 719 | for modIdx in 0..module::entryCount(graph) { |
| 720 | if let entry = module::get(graph, modIdx as u16) { |
| 721 | if entry.packageId == packageId { |
| 722 | collectModuleTests(graph, entry, tests, &mut count); |
| 723 | } |
| 724 | } |
| 725 | } |
| 726 | return count; |
| 727 | } |
| 728 | |
| 729 | /// Synthesize a `testing::test("mod", "name", mod::fn)` call for one test. |
| 730 | unsafe fn synthTestCall(arena: &mut ast::NodeArena, desc: &TestDesc) -> *ast::Node { |
| 731 | let callee = synthScopeAccess(arena, &["testing", "test"]); |
| 732 | let modStr = il::formatQualifiedName( |
| 733 | &mut arena.arena, |
| 734 | &desc.modPath[..desc.modPath.len - 1], |
| 735 | desc.modPath[desc.modPath.len - 1] |
| 736 | ); |
| 737 | let modArg = ast::synthNode(arena, ast::NodeValue::String(modStr)); |
| 738 | let nameArg = ast::synthNode(arena, ast::NodeValue::String(desc.fnName)); |
| 739 | |
| 740 | // Intra-package path: skip the package name prefix. |
| 741 | let mut funcPath: [*[u8]; 16] = [""; 16]; |
| 742 | for j in 1..desc.modPath.len { |
| 743 | set funcPath[j - 1] = desc.modPath[j]; |
| 744 | } |
| 745 | set funcPath[desc.modPath.len - 1] = desc.fnName; |
| 746 | let funcArg = synthScopeAccess(arena, &funcPath[..desc.modPath.len]); |
| 747 | |
| 748 | let a = alloc::arenaAllocator(&mut arena.arena); |
| 749 | let args = ast::nodeSlice(arena, 3) |
| 750 | .append(modArg, a) |
| 751 | .append(nameArg, a) |
| 752 | .append(funcArg, a); |
| 753 | |
| 754 | return ast::synthNode(arena, ast::NodeValue::Call(ast::Call { callee, args })); |
| 755 | } |
| 756 | |
| 757 | /// Inject a test runner into the entry package's root module. |
| 758 | /// |
| 759 | /// Scans the entry package for `@test fn` declarations, then appends |
| 760 | /// a synthetic entry point to the root module's AST block: |
| 761 | /// |
| 762 | /// ``` |
| 763 | /// @default fn #testMain() -> i32 { |
| 764 | /// return testing::runAllTests(&[ |
| 765 | /// testing::test("std::tests", "testFoo", tests::testFoo), |
| 766 | /// ... |
| 767 | /// ]); |
| 768 | /// } |
| 769 | /// ``` |
| 770 | /// |
| 771 | /// Uses `#`-prefixed names to avoid conflicts with user code. |
| 772 | unsafe fn generateTestRunner 'permission (ctx: *unsafe mut CompileContext 'permission, arena: &mut ast::NodeArena) throws (Error) { |
| 773 | let entryPkg = try getEntryPackage(ctx); |
| 774 | let root = try getRootModule(&entryPkg, &ctx.graph); |
| 775 | |
| 776 | // Collect test functions from the entry package's modules. |
| 777 | let mut tests: [?TestDesc; MAX_TESTS] = [nil; MAX_TESTS]; |
| 778 | let testCount = collectPackageTests(&ctx.graph, entryPkg.id, &mut tests[..]); |
| 779 | if testCount == 0 { |
| 780 | throw error(&["fatal:", "no test functions found"]); |
| 781 | } |
| 782 | let mut countBuf: [u8; 10] = [0; 10]; |
| 783 | let start = fmt::formatU32(testCount, &mut countBuf[..]); |
| 784 | io::printError("radiance: "); |
| 785 | io::printError(entryPkg.name); |
| 786 | io::printError(": found "); |
| 787 | io::printError(&countBuf[start..]); |
| 788 | io::printError(" test(s)\n"); |
| 789 | |
| 790 | // Synthesize the `@default` function and append to the root module. |
| 791 | let fnDecl = synthTestMainFn(arena, &tests[..testCount]); |
| 792 | |
| 793 | let updatedRoot = injectIntoBlock(root.ast, arena, fnDecl); |
| 794 | try module::setAst(&mut ctx.graph, root.entry.id, updatedRoot) catch { |
| 795 | throw error(&["failed to set test runner AST"]); |
| 796 | }; |
| 797 | } |
| 798 | |
| 799 | /// Synthesize the test entry point. |
| 800 | unsafe fn synthTestMainFn(arena: &mut ast::NodeArena, tests: &[?TestDesc]) -> *ast::Node { |
| 801 | // Build array literal: `[testing::test(...), ...]`. |
| 802 | let a = alloc::arenaAllocator(&mut arena.arena); |
| 803 | let mut elements = ast::nodeSlice(arena, tests.len as u32); |
| 804 | for i in 0..tests.len { |
| 805 | let desc = tests[i] else panic "synthTestMainFn: missing active test"; |
| 806 | elements.append(synthTestCall(arena, &desc), a); |
| 807 | } |
| 808 | let arrayLit = ast::synthNode(arena, ast::NodeValue::ArrayLit(elements)); |
| 809 | |
| 810 | // Build: `&[...]`. |
| 811 | let testsRef = ast::synthNode(arena, ast::NodeValue::AddressOf(ast::AddressOf { |
| 812 | target: arrayLit, kind: ast::AddressKind::Shared, permission: nil, |
| 813 | })); |
| 814 | |
| 815 | // Build: `testing::runAllTests(&[...])`. |
| 816 | let runFn = synthScopeAccess(arena, &["testing", "runAllTests"]); |
| 817 | let callArgs = ast::nodeSlice(arena, 1).append(testsRef, a); |
| 818 | let callExpr = ast::synthNode(arena, ast::NodeValue::Call(ast::Call { |
| 819 | callee: runFn, args: callArgs, |
| 820 | })); |
| 821 | |
| 822 | // Build: `return testing::runAllTests(&[...]);` |
| 823 | let retStmt = ast::synthNode(arena, ast::NodeValue::Return { value: callExpr }); |
| 824 | let bodyStmts = ast::nodeSlice(arena, 1).append(retStmt, a); |
| 825 | let fnBody = ast::synthNode(arena, ast::NodeValue::Block(ast::Block { statements: bodyStmts, isUnsafe: false })); |
| 826 | |
| 827 | // Build: `unsafe fn #testMain() -> i32` |
| 828 | let fnName = ast::synthNode(arena, ast::NodeValue::Ident(strings::intern(&mut STRING_POOL, "#testMain"))); |
| 829 | let returnType = ast::synthNode(arena, ast::NodeValue::TypeSig(ast::TypeSig::Integer { |
| 830 | width: 4, sign: ast::Signedness::Signed, |
| 831 | })); |
| 832 | let fnSig = ast::FnSig { |
| 833 | params: ast::nodeSlice(arena, 0), |
| 834 | returnType, |
| 835 | throwList: ast::nodeSlice(arena, 0), |
| 836 | }; |
| 837 | |
| 838 | // Entry and function safety attributes. |
| 839 | let attrNode = ast::synthNode(arena, ast::NodeValue::Attribute(ast::Attribute::Default)); |
| 840 | let unsafeAttr = ast::synthNode(arena, ast::NodeValue::Attribute(ast::Attribute::Unsafe)); |
| 841 | let attrList = ast::nodeSlice(arena, 2).append(attrNode, a).append(unsafeAttr, a); |
| 842 | let fnAttrs = ast::Attributes { list: attrList }; |
| 843 | |
| 844 | return ast::synthNode(arena, ast::NodeValue::FnDecl(ast::FnDecl { |
| 845 | name: fnName, regions: &[], sig: fnSig, body: fnBody, attrs: fnAttrs, |
| 846 | })); |
| 847 | } |
| 848 | |
| 849 | /// Build a block node with a declaration appended to its statement list. |
| 850 | unsafe fn injectIntoBlock( |
| 851 | blockNode: *ast::Node, |
| 852 | arena: &mut ast::NodeArena, |
| 853 | decl: *ast::Node |
| 854 | ) -> *ast::Node { |
| 855 | let case ast::NodeValue::Block(block) = blockNode.value else { |
| 856 | panic "injectIntoBlock: expected Block node"; |
| 857 | }; |
| 858 | let allocator = alloc::arenaAllocator(&mut arena.arena); |
| 859 | let mut stmts = ast::nodeSlice(arena, block.statements.len + 1); |
| 860 | for stmt in block.statements { |
| 861 | stmts.append(stmt, allocator); |
| 862 | } |
| 863 | stmts.append(decl, allocator); |
| 864 | return ast::allocNode(arena, blockNode.span, ast::NodeValue::Block( |
| 865 | ast::Block { statements: stmts, isUnsafe: block.isUnsafe } |
| 866 | )); |
| 867 | } |
| 868 | |
| 869 | /// Write a self-contained RV64 image containing text and data sections. |
| 870 | fn writeImage( |
| 871 | code: &[u8], |
| 872 | roData: &[u8], |
| 873 | rwData: &[u8], |
| 874 | path: *[u8] |
| 875 | ) -> bool { |
| 876 | let header = rv64::imageHeader(code.len, roData.len, rwData.len); |
| 877 | let mut headerBytes = [0 as u8; IMAGE_HEADER_SIZE]; |
| 878 | assert headerBytes.len == header.len * @sizeOf(u32); |
| 879 | for word, index in header { |
| 880 | for byte in 0..@sizeOf(u32) { |
| 881 | set headerBytes[index * @sizeOf(u32) + byte] = (word >> (byte * 8)) as u8; |
| 882 | } |
| 883 | } |
| 884 | |
| 885 | let fd = unix::openOpts(path, unix::OpenFlags(*unix::O_WRONLY | *unix::O_CREAT | *unix::O_TRUNC), 420); |
| 886 | if fd < 0 { |
| 887 | return false; |
| 888 | } |
| 889 | let written = unix::writeAll(fd, &headerBytes[..]) and unix::writeAll(fd, code) |
| 890 | and unix::writeAll(fd, roData) and unix::writeAll(fd, rwData); |
| 891 | let closed = unix::close(fd) == 0; |
| 892 | return written and closed; |
| 893 | } |
| 894 | |
| 895 | /// Write a data section to a file at `basePath` + `ext`. |
| 896 | /// Empty data truncates any stale sidecar left by an earlier build. |
| 897 | fn writeDataWithExt( |
| 898 | data: &[u8], |
| 899 | basePath: *[u8], |
| 900 | ext: *[u8] |
| 901 | ) throws (Error) { |
| 902 | let mut path: [u8; MAX_PATH_LEN] = [0; MAX_PATH_LEN]; |
| 903 | let mut pos: u32 = 0; |
| 904 | |
| 905 | set pos += try! mem::copy(&mut path[pos..], basePath); |
| 906 | set pos += try! mem::copy(&mut path[pos..], ext); |
| 907 | set path[pos] = 0; // Null-terminate for syscall. |
| 908 | |
| 909 | if not unix::writeFile(&path[..pos], data) { |
| 910 | throw error(&["fatal:", "failed to write data file"]); |
| 911 | } |
| 912 | } |
| 913 | |
| 914 | /// Serialize debug entries and write the `.debug` file. |
| 915 | /// Resolves module IDs to file paths via the module graph. |
| 916 | /// Format per entry is `{pc: u32, offset: u32, filePath: [u8], NULL}`. |
| 917 | fn writeDebugInfo 'permission ( |
| 918 | entries: &[types::DebugEntry], |
| 919 | graph: &module::ModuleGraph 'permission, |
| 920 | basePath: *[u8], |
| 921 | buf: &mut [u8] |
| 922 | ) throws (Error) { |
| 923 | if entries.len == 0 { |
| 924 | return; |
| 925 | } |
| 926 | // The caller supplies the serialization buffer. |
| 927 | let mut pos: u32 = 0; |
| 928 | |
| 929 | for i in 0..entries.len { |
| 930 | let entry = &entries[i]; |
| 931 | let modEntry = module::get(graph, entry.moduleId) else { |
| 932 | panic "writeDebugInfo: module not found for debug entry"; |
| 933 | }; |
| 934 | for value in [entry.pc, entry.offset] { |
| 935 | for i in 0..@sizeOf(u32) { |
| 936 | set buf[pos] = (value >> (i * 8)) as u8; |
| 937 | set pos += 1; |
| 938 | } |
| 939 | } |
| 940 | set pos += try! mem::copy(&mut buf[pos..], modEntry.filePath); |
| 941 | |
| 942 | set buf[pos] = 0; |
| 943 | set pos += 1; |
| 944 | } |
| 945 | try writeDataWithExt(&buf[..pos], basePath, DEBUG_EXT); |
| 946 | } |
| 947 | |
| 948 | /// Run the resolver on the parsed modules. |
| 949 | unsafe fn runResolver 'arena 'permission ( |
| 950 | ctx: &CompileContext 'permission, |
| 951 | mainArena: &'arena mut alloc::Arena, |
| 952 | nodeCount: u32 |
| 953 | ) -> resolver::Resolver 'arena throws (Error) { |
| 954 | let entryPkg = try getEntryPackage(ctx); |
| 955 | |
| 956 | pkgLog(&entryPkg, &["resolving", ".."]); |
| 957 | |
| 958 | let nodeDataSize = nodeCount * @sizeOf(resolver::NodeData); |
| 959 | let nodeDataPtr = try! alloc::alloc(&mut *mainArena, nodeDataSize, @alignOf(resolver::NodeData)); |
| 960 | let nodeData = @sliceOf(nodeDataPtr as *mut resolver::NodeData, nodeCount); |
| 961 | let storage = resolver::ResolverStorage { |
| 962 | nodeData, |
| 963 | pkgScope: &mut RESOLVER_PKG_SCOPE, |
| 964 | errors: &mut RESOLVER_ERRORS[..], |
| 965 | }; |
| 966 | let mut res = resolver::resolver(mainArena, storage, ctx.config); |
| 967 | |
| 968 | // Build the semantic package list consumed by the resolver. |
| 969 | let mut resolverPkgs: [resolver::Pkg; MAX_PACKAGES] = undefined; |
| 970 | let mut resolverPackageCount: u32 = 0; |
| 971 | for i in 0..ctx.packageCount { |
| 972 | let pkg = &ctx.packages[i]; |
| 973 | let root = try getRootModule(pkg, &ctx.graph); |
| 974 | |
| 975 | set resolverPkgs[resolverPackageCount] = resolver::Pkg { |
| 976 | rootEntry: root.entry, |
| 977 | rootAst: root.ast, |
| 978 | }; |
| 979 | set resolverPackageCount += 1; |
| 980 | } |
| 981 | |
| 982 | // Resolve all packages. |
| 983 | // TODO: Fix this error printing dance. |
| 984 | let diags = try resolver::resolve(&mut res, &ctx.graph, &resolverPkgs[..resolverPackageCount]) catch { |
| 985 | let diags = resolver::diagnostics(&mut res); |
| 986 | resolver::printer::printDiagnostics(&diags, &res, &ctx.graph); |
| 987 | throw Error::Other; |
| 988 | }; |
| 989 | if not resolver::success(&diags) { |
| 990 | resolver::printer::printDiagnostics(&diags, &res, &ctx.graph); |
| 991 | let mut countBuf: [u8; 10] = undefined; |
| 992 | let start = fmt::formatU32(diags.errors.len, &mut countBuf[..]); |
| 993 | io::printError("radiance: failed: "); |
| 994 | io::printError(&countBuf[start..]); |
| 995 | io::printError(" errors\n"); |
| 996 | throw Error::Other; |
| 997 | } |
| 998 | return res; |
| 999 | } |
| 1000 | |
| 1001 | /// Assemble one `.ras` input and merge it into the active code generator. |
| 1002 | /// |
| 1003 | /// Text symbols are appended to `generator`. Data emitted by the assembler is |
| 1004 | /// copied into `ASM_RO_DATA_BUF` at `*asmDataLen`, and `*asmDataLen` is advanced |
| 1005 | /// so the next assembly module receives the correct rodata base address. |
| 1006 | unsafe fn assembleAsmModule( |
| 1007 | generator: &mut rv64::Generator, |
| 1008 | pkg: &package::Package, |
| 1009 | path: *[u8], |
| 1010 | asmDataLen: &mut u32, |
| 1011 | arena: &mut alloc::Arena |
| 1012 | ) throws (Error) { |
| 1013 | pkgLog(pkg, &["asm:", "parsing", "(", path, ")", ".."]); |
| 1014 | |
| 1015 | let sourceLen = unix::readFile(path, &mut ASM_SOURCE_BUF[..]) else { |
| 1016 | throw error(&["error reading assembly file"]); |
| 1017 | }; |
| 1018 | let input = &ASM_SOURCE_BUF[..sourceLen]; |
| 1019 | if input.len == ASM_SOURCE_BUF.len { |
| 1020 | throw error(&["fatal:", "assembly source too large:", path]); |
| 1021 | } |
| 1022 | // Assembly symbols borrow source bytes until final linking. |
| 1023 | let buffer = try alloc::allocSlice(arena, 1, 1, input.len) catch { |
| 1024 | throw error(&["assembly source workspace exhausted"]); |
| 1025 | }; |
| 1026 | let source = buffer as *mut [u8]; |
| 1027 | try! mem::copy(source, input); |
| 1028 | let program = try asm::assemble( |
| 1029 | asm::scanner::SourceKind::File { path }, |
| 1030 | source, |
| 1031 | &mut ASM_TEXT_BUF[..], |
| 1032 | &mut ASM_DATA_BUF[..], |
| 1033 | arena, |
| 1034 | &mut STRING_POOL, |
| 1035 | rv64::RO_DATA_BASE + *asmDataLen |
| 1036 | ) catch { |
| 1037 | throw error(&["assembly failed:", path]); |
| 1038 | }; |
| 1039 | if *asmDataLen + program.data.len > ASM_RO_DATA_BUF.len { |
| 1040 | throw error(&["fatal:", "assembly rodata too large"]); |
| 1041 | } |
| 1042 | try! mem::copy(&mut ASM_RO_DATA_BUF[*asmDataLen..], program.data); |
| 1043 | set *asmDataLen += program.data.len; |
| 1044 | |
| 1045 | rv64::addAssembly(generator, program); |
| 1046 | } |
| 1047 | |
| 1048 | /// Assemble all inputs collected in the package inputs. |
| 1049 | unsafe fn assembleAsmInputs 'permission ( |
| 1050 | ctx: &CompileContext 'permission, |
| 1051 | generator: &mut rv64::Generator, |
| 1052 | asmDataLen: &mut u32, |
| 1053 | arena: &mut alloc::Arena |
| 1054 | ) -> *[u8] throws (Error) { |
| 1055 | for i in 0..ctx.packageCount { |
| 1056 | for j in 0..ctx.inputs[i].asmPathCount { |
| 1057 | try assembleAsmModule( |
| 1058 | generator, |
| 1059 | &ctx.packages[i], |
| 1060 | ctx.inputs[i].asmPaths[j], |
| 1061 | asmDataLen, |
| 1062 | arena |
| 1063 | ); |
| 1064 | } |
| 1065 | } |
| 1066 | return &ASM_RO_DATA_BUF[..*asmDataLen]; |
| 1067 | } |
| 1068 | |
| 1069 | /// Generate dependency packages before the entry package. |
| 1070 | unsafe fn generateAllPackagesInto 'arena 'phase 'permission ( |
| 1071 | ctx: &CompileContext 'permission, |
| 1072 | low: &mut lower::Lowerer 'arena 'phase, |
| 1073 | generator: &mut rv64::Generator, |
| 1074 | fnArena: &mut alloc::Arena |
| 1075 | ) throws (Error) where 'arena: 'phase { |
| 1076 | let entryIdx = ctx.entryPkgIdx else panic "generateAllPackagesInto: no entry package"; |
| 1077 | for i in 0..ctx.packageCount { |
| 1078 | if i <> entryIdx { |
| 1079 | try generatePackageInto(ctx, low, &ctx.packages[i], false, generator, fnArena); |
| 1080 | } |
| 1081 | } |
| 1082 | try generatePackageInto(ctx, low, &ctx.packages[entryIdx], true, generator, fnArena); |
| 1083 | } |
| 1084 | |
| 1085 | /// Generate all functions in one package. |
| 1086 | unsafe fn generatePackageInto 'arena 'phase 'permission ( |
| 1087 | ctx: &CompileContext 'permission, |
| 1088 | low: &mut lower::Lowerer 'arena 'phase, |
| 1089 | pkg: &package::Package, |
| 1090 | isEntry: bool, |
| 1091 | generator: &mut rv64::Generator, |
| 1092 | fnArena: &mut alloc::Arena |
| 1093 | ) throws (Error) where 'arena: 'phase { |
| 1094 | let rootId = pkg.rootModuleId else throw error(&["no root module found"]); |
| 1095 | lower::setPackage(low, pkg.name); |
| 1096 | try generateModuleTree(ctx, low, rootId, isEntry, pkg, generator, fnArena); |
| 1097 | } |
| 1098 | |
| 1099 | /// Lower and consume each function before visiting the next declaration. |
| 1100 | unsafe fn generateModuleTree 'arena 'phase 'permission ( |
| 1101 | ctx: &CompileContext 'permission, |
| 1102 | low: &mut lower::Lowerer 'arena 'phase, |
| 1103 | modId: u16, |
| 1104 | isRoot: bool, |
| 1105 | pkg: &package::Package, |
| 1106 | generator: &mut rv64::Generator, |
| 1107 | fnArena: &mut alloc::Arena |
| 1108 | ) throws (Error) where 'arena: 'phase { |
| 1109 | let entry = module::get(&ctx.graph, modId) |
| 1110 | else throw error(&["module entry not found"]); |
| 1111 | let modAst = module::astFor(&ctx.graph, entry) |
| 1112 | else throw error(&["module has no AST"]); |
| 1113 | pkgLog(pkg, &["lowering", "(", entry.filePath, ")", ".."]); |
| 1114 | set low.currentMod = modId; |
| 1115 | let mut cursor = try lower::moduleCursor(modAst, isRoot) catch err { |
| 1116 | io::printError("radiance: internal error during lowering: "); |
| 1117 | lower::printError(err); |
| 1118 | io::printError("\n"); |
| 1119 | throw Error::Other; |
| 1120 | }; |
| 1121 | loop { |
| 1122 | let result = try lower::lowerNext(low, &mut cursor, fnArena) catch err { |
| 1123 | io::printError("radiance: internal error during lowering: "); |
| 1124 | lower::printError(err); |
| 1125 | io::printError("\n"); |
| 1126 | throw Error::Other; |
| 1127 | }; |
| 1128 | let next = result else break; |
| 1129 | codegen::emit(generator, fnArena, &*next.function, next.role); |
| 1130 | } |
| 1131 | for i in 0..module::childCount(&ctx.graph, entry) { |
| 1132 | let childId = module::childAt(&ctx.graph, entry, i); |
| 1133 | try generateModuleTree(ctx, low, childId, false, pkg, generator, fnArena); |
| 1134 | } |
| 1135 | } |
| 1136 | |
| 1137 | /// Lower all packages while streaming each lowered function into RV64 codegen. |
| 1138 | unsafe fn lowerAndGenerateAllPackages 'arena 'permission ( |
| 1139 | ctx: &CompileContext 'permission, |
| 1140 | res: *unsafe mut resolver::Resolver 'arena, |
| 1141 | fnArena: &mut alloc::Arena, |
| 1142 | codegenOptions: CodegenOptions |
| 1143 | ) -> rv64::Program throws (Error) { |
| 1144 | let entryIdx = ctx.entryPkgIdx else { |
| 1145 | panic "lowerAndGenerateAllPackages: no entry package"; |
| 1146 | }; |
| 1147 | let entryPkg = &ctx.packages[entryIdx]; |
| 1148 | let startupPath = getEntryStartupPath(ctx); |
| 1149 | let options = lower::LowerOptions { debug: ctx.debug, buildTest: ctx.config.buildTest }; |
| 1150 | let storage = rv64::Storage { |
| 1151 | dataSyms: &mut CODEGEN_DATA_SYMS[..], |
| 1152 | dataSymEntries: &mut CODEGEN_DATA_SYM_ENTRIES[..], |
| 1153 | }; |
| 1154 | let mut entryPatch = rv64::EntryPatch::None; |
| 1155 | match codegenOptions.entryMode { |
| 1156 | case CodegenEntryMode::DefaultEntry => { |
| 1157 | set entryPatch = rv64::EntryPatch::Reserved(nil); |
| 1158 | } |
| 1159 | else => {} |
| 1160 | } |
| 1161 | let emitterStorage = emit::Storage { |
| 1162 | code: &mut CODEGEN_INSTRUCTIONS[..], |
| 1163 | pendingBranches: &mut CODEGEN_PENDING_BRANCHES[..], |
| 1164 | pendingCalls: &mut CODEGEN_PENDING_CALLS[..], |
| 1165 | pendingJumps: &mut CODEGEN_PENDING_JUMPS[..], |
| 1166 | pendingAddrLoads: &mut CODEGEN_PENDING_ADDR_LOADS[..], |
| 1167 | blockOffsets: &mut CODEGEN_BLOCK_OFFSETS[..], |
| 1168 | funcEntries: &mut CODEGEN_FUNC_ENTRIES[..], |
| 1169 | funcs: &mut CODEGEN_FUNCS[..], |
| 1170 | debugEntries: &mut CODEGEN_DEBUG_ENTRIES[..], |
| 1171 | }; |
| 1172 | let mut generator = rv64::beginProgramWithStorage( |
| 1173 | rv64::ProgramOptions { |
| 1174 | entryPatch, |
| 1175 | debug: codegenOptions.debug, |
| 1176 | placement: rv64::image::Placement::Hosted, |
| 1177 | }, |
| 1178 | emitterStorage |
| 1179 | ); |
| 1180 | let arena = (&mut *res.arena) as *unsafe mut alloc::Arena; |
| 1181 | let resolved: 'phase = &*res where 'arena: 'phase in { |
| 1182 | let mut low = lower::lowerer(resolved, entryPkg.name, arena, options); |
| 1183 | let mut asmDataLen: u32 = 0; |
| 1184 | if let path = startupPath { |
| 1185 | try assembleAsmModule(&mut generator, entryPkg, path, &mut asmDataLen, arena); |
| 1186 | } |
| 1187 | try generateAllPackagesInto( |
| 1188 | ctx, &mut low, &mut generator, fnArena |
| 1189 | ); |
| 1190 | let asmData = try assembleAsmInputs(ctx, &mut generator, &mut asmDataLen, arena); |
| 1191 | match generator.entryPatch { |
| 1192 | case rv64::EntryPatch::Reserved(targetName) => { |
| 1193 | if targetName == nil { |
| 1194 | throw error(&["fatal:", "no default function found"]); |
| 1195 | } |
| 1196 | } |
| 1197 | else => { |
| 1198 | } |
| 1199 | } |
| 1200 | if let path = codegenOptions.logPath { |
| 1201 | pkgLog(entryPkg, &["generating code", "(", path, ")", ".."]); |
| 1202 | } |
| 1203 | return try rv64::finishProgram( |
| 1204 | generator, low.data, storage, asmData, |
| 1205 | &mut RO_DATA_BUF[..], &mut RW_DATA_BUF[..] |
| 1206 | ) catch err { |
| 1207 | match err { |
| 1208 | case rv64::Error::Allocation => throw error(&["code generation workspace exhausted"]), |
| 1209 | case rv64::Error::Capacity => throw error(&["code generation output capacity exceeded"]), |
| 1210 | case rv64::Error::Symbol => throw error(&["code generation has an unresolved symbol"]), |
| 1211 | case rv64::Error::Relocation => throw error(&["code generation relocation is out of range"]), |
| 1212 | case rv64::Error::Image(_) => throw error(&["code generation image layout is invalid"]), |
| 1213 | case rv64::Error::Data(_) => throw error(&["code generation data layout is invalid"]), |
| 1214 | } |
| 1215 | }; |
| 1216 | } |
| 1217 | } |
| 1218 | |
| 1219 | /// Source exports selected for one binary RIL package. |
| 1220 | record PackageExports: Copy { |
| 1221 | /// Number of initialized entries in the caller's export table. |
| 1222 | count: u32, |
| 1223 | /// Default function entry. |
| 1224 | entry: ?*[u8], |
| 1225 | } |
| 1226 | |
| 1227 | /// Collect exported definitions and the default entry from a package's source modules. |
| 1228 | unsafe fn packageExports 'permission ( |
| 1229 | ctx: &CompileContext 'permission, |
| 1230 | pkg: &package::Package, |
| 1231 | ilProgram: &il::Program, |
| 1232 | exports: &mut [binary::Export], |
| 1233 | arena: &mut alloc::Arena |
| 1234 | ) -> PackageExports throws (Error) { |
| 1235 | let mut count: u32 = 0; |
| 1236 | let mut entryName: ?*[u8] = nil; |
| 1237 | for i in 0..module::entryCount(&ctx.graph) { |
| 1238 | let modEntry = module::get(&ctx.graph, i as u16) else continue; |
| 1239 | if modEntry.packageId <> pkg.id { |
| 1240 | continue; |
| 1241 | } |
| 1242 | let root = module::astFor(&ctx.graph, modEntry) else continue; |
| 1243 | let case ast::NodeValue::Block(block) = root.value else continue; |
| 1244 | for node in block.statements { |
| 1245 | let mut ident: ?*ast::Node = nil; |
| 1246 | let mut attrs: ?ast::Attributes = nil; |
| 1247 | let mut kind = binary::ExportKind::Function; |
| 1248 | match node.value { |
| 1249 | case ast::NodeValue::FnDecl(decl) => { |
| 1250 | set ident = decl.name; |
| 1251 | set attrs = decl.attrs; |
| 1252 | }, |
| 1253 | case ast::NodeValue::ConstDecl(decl) => { |
| 1254 | set ident = decl.ident; set attrs = decl.attrs; set kind = binary::ExportKind::Data; |
| 1255 | }, |
| 1256 | case ast::NodeValue::StaticDecl(decl) => { |
| 1257 | set ident = decl.ident; set attrs = decl.attrs; set kind = binary::ExportKind::Data; |
| 1258 | }, |
| 1259 | else => continue, |
| 1260 | } |
| 1261 | let attributes = attrs else continue; |
| 1262 | if ast::attributesContains(&attributes, ast::Attribute::Intrinsic) { |
| 1263 | continue; |
| 1264 | } |
| 1265 | let isDefault = ast::attributesContains(&attributes, ast::Attribute::Default) |
| 1266 | and pkg.rootModuleId == modEntry.id; |
| 1267 | if not isDefault and not ast::attributesContains(&attributes, ast::Attribute::Export) { |
| 1268 | continue; |
| 1269 | } |
| 1270 | let nameNode = ident else continue; |
| 1271 | let case ast::NodeValue::Ident(name) = nameNode.value else continue; |
| 1272 | let qualified = il::formatQualifiedName(arena, module::moduleQualifiedPath(modEntry), name); |
| 1273 | let mut present = false; |
| 1274 | match kind { |
| 1275 | case binary::ExportKind::Function => { |
| 1276 | for func in ilProgram.fns { |
| 1277 | if mem::eq(func.name, qualified) { |
| 1278 | set present = true; |
| 1279 | } |
| 1280 | } |
| 1281 | }, |
| 1282 | case binary::ExportKind::Data => { |
| 1283 | for item in ilProgram.data { |
| 1284 | if mem::eq(item.name, qualified) { |
| 1285 | set present = true; |
| 1286 | } |
| 1287 | } |
| 1288 | }, |
| 1289 | } |
| 1290 | if not present { |
| 1291 | continue; |
| 1292 | } |
| 1293 | if count == exports.len { |
| 1294 | throw error(&["too many package exports"]); |
| 1295 | } |
| 1296 | set exports[count] = binary::Export { name: qualified, kind }; |
| 1297 | set count += 1; |
| 1298 | if isDefault { |
| 1299 | set entryName = qualified; |
| 1300 | } |
| 1301 | } |
| 1302 | } |
| 1303 | return PackageExports { count, entry: entryName }; |
| 1304 | } |
| 1305 | |
| 1306 | /// Write encoded binary RIL bytes into an existing directory. |
| 1307 | fn writePackage(bytes: &[u8], directory: *[u8], name: *[u8]) throws (Error) { |
| 1308 | let mut path = [0 as u8; MAX_PATH_LEN]; |
| 1309 | let mut pos: u32 = 0; |
| 1310 | for part in &[directory, "/", name, ".ril"] { |
| 1311 | set pos += try mem::copy(&mut path[pos..MAX_PATH_LEN - 1], part) catch { |
| 1312 | throw error(&["binary RIL output path is too long"]); |
| 1313 | }; |
| 1314 | } |
| 1315 | set path[pos] = 0; |
| 1316 | if not unix::writeFile(&path[..pos], bytes) { |
| 1317 | throw error(&["cannot write binary RIL package", name]); |
| 1318 | } |
| 1319 | } |
| 1320 | |
| 1321 | /// Emit one binary RIL file per package into an existing directory. |
| 1322 | unsafe fn emitPackages 'arena 'permission ( |
| 1323 | ctx: &CompileContext 'permission, |
| 1324 | res: *unsafe mut resolver::Resolver 'arena, |
| 1325 | directory: *[u8] |
| 1326 | ) throws (Error) { |
| 1327 | for i in 0..ctx.packageCount { |
| 1328 | if ctx.inputs[i].asmPathCount > 0 or ctx.inputs[i].startupPath <> nil { |
| 1329 | throw error(&["binary RIL output requires Radiance source modules"]); |
| 1330 | } |
| 1331 | } |
| 1332 | let unified = try lowerAllPackages(ctx, res); |
| 1333 | let allocator = alloc::arenaAllocator(&mut *res.arena); |
| 1334 | for i in 0..ctx.packageCount { |
| 1335 | let pkg = &ctx.packages[i]; |
| 1336 | let mut dataItems: *mut [il::Data] = &mut []; |
| 1337 | let mut functions: *unsafe mut [*unsafe il::Fn] = &mut []; |
| 1338 | for item in unified.data { |
| 1339 | if ownsSymbol(pkg.name, item.name) { |
| 1340 | dataItems.append(item, allocator); |
| 1341 | } |
| 1342 | } |
| 1343 | for func in unified.fns { |
| 1344 | if ownsSymbol(pkg.name, func.name) { |
| 1345 | functions.append(func, allocator); |
| 1346 | } |
| 1347 | } |
| 1348 | let local = il::Program { data: &dataItems[..], fns: functions }; |
| 1349 | let selected = try packageExports(ctx, pkg, &local, &mut PACKAGE_EXPORTS[..], &mut *res.arena); |
| 1350 | let mut dependencies: [*[u8]; MAX_PACKAGES] = [""; MAX_PACKAGES]; |
| 1351 | let symbolTable: 'names = &mut PACKAGE_SYMBOLS[..], dependencyTable = &mut dependencies[..] in { |
| 1352 | let mut names = collect::new(symbolTable, dependencyTable); |
| 1353 | let image = try collect::package(&mut names, pkg.name, local, &PACKAGE_EXPORTS[..selected.count], selected.entry) catch { |
| 1354 | throw error(&["cannot collect binary RIL package", pkg.name]); |
| 1355 | }; |
| 1356 | let length = try program::encode(&mut FN_ARENA[..], &image) catch { |
| 1357 | throw error(&["binary RIL output capacity exceeded", pkg.name]); |
| 1358 | }; |
| 1359 | try writePackage(&FN_ARENA[..length], directory, pkg.name); |
| 1360 | } |
| 1361 | } |
| 1362 | } |
| 1363 | |
| 1364 | /// Match a qualified definition to its package name. |
| 1365 | fn ownsSymbol(owner: *[u8], name: *[u8]) -> bool { |
| 1366 | let suffix = mem::stripPrefix(owner, name) else return false; |
| 1367 | return suffix.len > 2 and suffix[0] == ':' and suffix[1] == ':'; |
| 1368 | } |
| 1369 | |
| 1370 | /// Lower, optionally dump, and optionally generate binary output. |
| 1371 | unsafe fn compile 'arena 'permission ( |
| 1372 | ctx: &CompileContext 'permission, |
| 1373 | res: *unsafe mut resolver::Resolver 'arena, |
| 1374 | fnArena: &mut alloc::Arena |
| 1375 | ) throws (Error) { |
| 1376 | let entryPkg = try getEntryPackage(ctx); |
| 1377 | if let directory = ctx.rilDirectory { |
| 1378 | try emitPackages(ctx, res, directory); |
| 1379 | return; |
| 1380 | } |
| 1381 | let mut out = sexpr::Stdout {}; |
| 1382 | |
| 1383 | if ctx.dump == Dump::Il { |
| 1384 | // Lower all packages into a single unified IL program for dumping. |
| 1385 | let program = try lowerAllPackages(ctx, res); |
| 1386 | il::printer::printProgram(&mut out, &program); |
| 1387 | io::print("\n"); |
| 1388 | return; |
| 1389 | } |
| 1390 | if ctx.dump == Dump::Asm { |
| 1391 | let result = try lowerAndGenerateAllPackages( |
| 1392 | ctx, res, fnArena, CodegenOptions { |
| 1393 | logPath: nil, |
| 1394 | debug: false, |
| 1395 | entryMode: CodegenEntryMode::None, |
| 1396 | } |
| 1397 | ); |
| 1398 | printer::printCodeTo(&mut out, entryPkg.name, result.code, result.funcs); |
| 1399 | io::print("\n"); |
| 1400 | |
| 1401 | return; |
| 1402 | } |
| 1403 | // Generate binary output if path specified. |
| 1404 | let outPath = ctx.outputPath else { |
| 1405 | try lowerAllPackages(ctx, res); |
| 1406 | return; |
| 1407 | }; |
| 1408 | let startupPath = getEntryStartupPath(ctx); |
| 1409 | let result = try lowerAndGenerateAllPackages( |
| 1410 | ctx, res, fnArena, CodegenOptions { |
| 1411 | logPath: outPath, |
| 1412 | debug: ctx.debug, |
| 1413 | entryMode: CodegenEntryMode::None |
| 1414 | if startupPath <> nil |
| 1415 | else CodegenEntryMode::DefaultEntry, |
| 1416 | } |
| 1417 | ); |
| 1418 | |
| 1419 | let codeBytes = @sliceOf(result.code.ptr as *u8, result.code.len * rv64::INSTR_SIZE as u32); |
| 1420 | if not writeImage( |
| 1421 | codeBytes, |
| 1422 | &RO_DATA_BUF[..result.roDataSize], |
| 1423 | &RW_DATA_BUF[..result.rwDataSize], |
| 1424 | outPath |
| 1425 | ) { |
| 1426 | throw error(&["fatal:", "failed to write output file"]); |
| 1427 | } |
| 1428 | |
| 1429 | // Write debug info file if enabled. |
| 1430 | if ctx.debug { |
| 1431 | let buf = alloc::remainingBuf(&mut *res.arena); |
| 1432 | try writeDebugInfo(result.debugEntries, &ctx.graph, outPath, &mut buf[..]); |
| 1433 | } |
| 1434 | pkgLog(&entryPkg, &["ok", "(", outPath, ")"]); |
| 1435 | } |
| 1436 | |
| 1437 | @default unsafe fn main(env: *sys::Env) -> i32 { |
| 1438 | let mut owner = module::Permission {}; |
| 1439 | let permission: 'permission = &mut owner in { |
| 1440 | let mut arena = ast::nodeArena(&mut TEMP_ARENA[..]); |
| 1441 | let mut ctx = try processCommand(env.args, &mut arena, permission) catch { |
| 1442 | return 1; |
| 1443 | }; |
| 1444 | match ctx.dump { |
| 1445 | case Dump::Ast => { |
| 1446 | try dumpAst(&ctx) catch { |
| 1447 | return 1; |
| 1448 | }; |
| 1449 | return 0; |
| 1450 | } |
| 1451 | case Dump::Graph => { |
| 1452 | dumpGraph(&ctx); |
| 1453 | return 0; |
| 1454 | } |
| 1455 | else => {} |
| 1456 | } |
| 1457 | // Generate test runner if in test mode. |
| 1458 | if ctx.config.buildTest { |
| 1459 | try generateTestRunner(&mut ctx, &mut arena) catch { |
| 1460 | return 1; |
| 1461 | }; |
| 1462 | } |
| 1463 | // Run resolution phase. |
| 1464 | let mut mainArena = alloc::new(&mut MAIN_ARENA[..]); |
| 1465 | let arenaRef: 'arena = &mut mainArena, context = &ctx in { |
| 1466 | let mut res = try runResolver( |
| 1467 | context, arenaRef, arena.nextId |
| 1468 | ) catch { |
| 1469 | return 1; |
| 1470 | }; |
| 1471 | let mut fnArena = alloc::new(&mut FN_ARENA[..]); |
| 1472 | |
| 1473 | // Lower, dump, and/or generate output. |
| 1474 | try compile(context, &mut res, &mut fnArena) catch { |
| 1475 | return 1; |
| 1476 | }; |
| 1477 | return 0; |
| 1478 | } |
| 1479 | } |
| 1480 | } |