From 8b99ee9930ecb031d0b14b5ae04cb9ae1512041c Mon Sep 17 00:00:00 2001 From: Araq Date: Fri, 5 Dec 2025 13:00:56 +0100 Subject: [PATCH] added 'nim nifc' command --- compiler/commands.nim | 4 ++ compiler/main.nim | 19 +++++- compiler/nifbackend.nim | 126 ++++++++++++++++++++++++++++++++++++++++ compiler/options.nim | 1 + 4 files changed, 149 insertions(+), 1 deletion(-) create mode 100644 compiler/nifbackend.nim diff --git a/compiler/commands.nim b/compiler/commands.nim index 8c3de1c174..b1e4bcd231 100644 --- a/compiler/commands.nim +++ b/compiler/commands.nim @@ -498,6 +498,7 @@ proc parseCommand*(command: string): Command = of "secret": cmdInteractive of "nop", "help": cmdNop of "jsonscript": cmdJsonscript + of "nifc": cmdNifC # generate C from NIF files else: cmdUnknown proc setCmd*(conf: ConfigRef, cmd: Command) = @@ -510,6 +511,9 @@ proc setCmd*(conf: ConfigRef, cmd: Command) = of cmdCompileToOC: conf.backend = backendObjc of cmdCompileToJS: conf.backend = backendJs of cmdCompileToNif: conf.backend = backendNif + of cmdNifC: + conf.backend = backendC # NIF to C compilation + conf.globalOptions.incl optCompress # enable NIF loading of cmdM: # cmdM requires optCompress for proper IC handling (include files, etc.) conf.globalOptions.incl optCompress diff --git a/compiler/main.nim b/compiler/main.nim index da6ae82ce7..8a7a4c83e5 100644 --- a/compiler/main.nim +++ b/compiler/main.nim @@ -30,7 +30,7 @@ import ic / [cbackend, integrity, navigator, ic] import ../dist/checksums/src/checksums/sha1 -import pipelines +import pipelines, nifbackend when not defined(leanCompiler): import docgen @@ -133,6 +133,19 @@ proc commandCompileToNif(graph: ModuleGraph) = setPipeLinePass(graph, NifgenPass) compilePipelineProject(graph) +proc commandNifC(graph: ModuleGraph) = + ## Generate C code from precompiled NIF files. + ## This is the new IC approach: compile modules to NIF first with `nim m`, + ## then generate C code from the entry.nif file with whole-program DCE. + let conf = graph.config + extccomp.initVars(conf) + + if not extccomp.ccHasSaneOverflow(conf): + conf.symbols.defineSymbol("nimEmulateOverflowChecks") + + # Use the NIF backend to generate C code + nifbackend.generateCode(graph, conf.projectMainIdx) + proc commandCompileToC(graph: ModuleGraph) = let conf = graph.config extccomp.initVars(conf) @@ -424,6 +437,10 @@ proc mainCommand*(graph: ModuleGraph) = graph.config.symbolFiles = disabledSf setUseIc(false) commandCheck(graph) + of cmdNifC: + # Generate C code from NIF files + wantMainModule(conf) + commandNifC(graph) of cmdParse: wantMainModule(conf) discard parseFile(conf.projectMainIdx, cache, conf) diff --git a/compiler/nifbackend.nim b/compiler/nifbackend.nim new file mode 100644 index 0000000000..7edcb19244 --- /dev/null +++ b/compiler/nifbackend.nim @@ -0,0 +1,126 @@ +# +# +# The Nim Compiler +# (c) Copyright 2025 Andreas Rumpf +# +# See the file "copying.txt", included in this +# distribution, for details about the copyright. +# + +## NIF-based C/C++ code generator backend. +## +## This module implements C code generation from precompiled NIF files. +## It traverses the module dependency graph starting from the main module +## and generates C code for all reachable modules. +## +## Usage: +## 1. Compile modules to NIF: nim m mymodule.nim +## 2. Generate C from NIF: nim nifc myproject.nim + +when defined(nimKochBootstrap): + # During bootstrap, NIF functionality is not available + import ".." / [ast, options, lineinfos, modulegraphs, pathutils, msgs] + + proc generateCode*(g: ModuleGraph; mainFileIdx: FileIndex) = + rawMessage(g.config, errGenerated, + "NIF backend not available during bootstrap build") + +else: + import std/[intsets, tables, sets, os] + + when defined(nimPreviewSlimSystem): + import std/assertions + + import ast, options, lineinfos, modulegraphs, cgendata, cgen, + pathutils, extccomp, msgs, modulepaths, idents, types, ast2nif + + proc loadModuleDependencies(g: ModuleGraph; mainFileIdx: FileIndex): seq[PSym] = + ## Traverse the module dependency graph using a stack. + ## Returns all modules that need code generation, in dependency order. + var visited = initIntSet() + var stack: seq[FileIndex] = @[mainFileIdx] + var modules: seq[PSym] = @[] + var cachedModules: seq[FileIndex] = @[] + + while stack.len > 0: + let fileIdx = stack.pop() + + if visited.containsOrIncl(int(fileIdx)): + continue + + # Load module from NIF + let module = moduleFromNifFile(g, fileIdx, cachedModules) + if module == nil: + continue + + modules.add module + + # Add dependencies to stack (they come from cachedModules) + for dep in cachedModules: + if not visited.contains(int(dep)): + stack.add dep + cachedModules.setLen(0) + + result = modules + + proc setupNifBackendModule(g: ModuleGraph; module: PSym): BModule = + ## Set up a BModule for code generation from a NIF module. + if g.backend == nil: + g.backend = cgendata.newModuleList(g) + result = cgen.newModule(BModuleList(g.backend), module, g.config) + + proc generateCodeForModule(g: ModuleGraph; module: PSym) = + ## Generate C code for a single module. + let moduleId = module.position + var bmod = BModuleList(g.backend).modules[moduleId] + if bmod == nil: + bmod = setupNifBackendModule(g, module) + + # Generate code for the module's top-level statements + if module.ast != nil: + cgen.genTopLevelStmt(bmod, module.ast) + + # Finalize the module + finalCodegenActions(g, bmod, newNodeI(nkStmtList, module.info)) + + # Generate dispatcher methods + for disp in getDispatchers(g): + genProcAux(bmod, disp) + + proc generateCode*(g: ModuleGraph; mainFileIdx: FileIndex) = + ## Main entry point for NIF-based C code generation. + ## Traverses the module dependency graph and generates C code. + + # Reset backend state + resetForBackend(g) + + # Load all modules in dependency order using stack traversal + let modules = loadModuleDependencies(g, mainFileIdx) + if modules.len == 0: + rawMessage(g.config, errGenerated, + "Cannot load NIF file for main module: " & toFullPath(g.config, mainFileIdx)) + return + + # Set up backend modules + for module in modules: + discard setupNifBackendModule(g, module) + + # Generate code for all modules except main (main goes last) + let mainModule = g.getModule(mainFileIdx) + for module in modules: + if module != mainModule: + generateCodeForModule(g, module) + + # Generate main module last (so all init procs are registered) + if mainModule != nil: + generateCodeForModule(g, mainModule) + + # Write C files + if g.backend != nil: + cgenWriteModules(g.backend, g.config) + + # Run C compiler + if g.config.cmd != cmdTcc: + extccomp.callCCompiler(g.config) + if not g.config.hcrOn: + extccomp.writeJsonBuildInstructions(g.config, g.cachedFiles) diff --git a/compiler/options.nim b/compiler/options.nim index 142080cc8f..930b1f543e 100644 --- a/compiler/options.nim +++ b/compiler/options.nim @@ -175,6 +175,7 @@ type cmdJsonscript # compile a .json build file # old unused: cmdInterpret, cmdDef: def feature (find definition for IDEs) cmdCompileToNif + cmdNifC # generate C code from NIF files const cmdBackends* = {cmdCompileToC, cmdCompileToCpp, cmdCompileToOC,