Merge commit '9cc3f24' into lang/foundations
This commit is contained in:
commit
1033c78db3
10 changed files with 4119 additions and 2288 deletions
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load diff
|
|
@ -280,7 +280,11 @@ entry {
|
|||
# -Wno-override-module: the emitted IR carries no target triple, so clang
|
||||
# substitutes the host's and warns about it on every single build. There is
|
||||
# nothing to act on — the host default is exactly what we want — so it is off.
|
||||
var cmd = `{cc} -O2 -Wno-override-module {ll}`
|
||||
# $LUDIC_OPT=1 is a developer's faster build (never a bundle's: ludic bundle sets 2)
|
||||
let opt = getenv_or("LUDIC_OPT", "2")
|
||||
let objs = split_compile(ll, out, opt) # on every core when it can (split_build.ludic)
|
||||
var cmd = `{cc} -O{opt} -Wno-override-module {ll}`
|
||||
if objs != null { cmd = `{cc} -O{opt} -Wno-override-module{objs}` }
|
||||
if g_target_win {
|
||||
# The Windows window: win32.ll (user32, gdi32 for the software present, xinput
|
||||
# for pads), and audio_win.ll when the program uses Audio.*, as on macOS.
|
||||
|
|
@ -389,6 +393,7 @@ entry {
|
|||
var line = cmd
|
||||
if host_is_windows() { line = `"{cmd}"` }
|
||||
let rc = run(line)
|
||||
if objs != null { split_clean(out) }
|
||||
if not save { remove_file(ll) }
|
||||
if rc != 0 { die("ludicc: link failed\n") }
|
||||
if natives_out != null {
|
||||
|
|
|
|||
87
selfhost/split_build.ludic
Normal file
87
selfhost/split_build.ludic
Normal file
|
|
@ -0,0 +1,87 @@
|
|||
# split_build.ludic - the program's IR compiled on every core: llvm-split cuts it into N parts, one
|
||||
# clang each runs at once, and the link takes their objects where it took the one .ll. Most of a
|
||||
# build was one clang -O2 on one file (the game: 32 s of 41). Needs an llvm-split and a clang of
|
||||
# the same LLVM (a newer LLVM's parts do not read in an older clang): $LUDIC_LLVM (a bin directory),
|
||||
# else Homebrew's. Anything missing, or a part that does not compile, falls back to the one clang.
|
||||
|
||||
function split_there(p: pointer) -> bool {
|
||||
let f = file_open(p, "rb")
|
||||
if f == null { return false }
|
||||
file_close(f)
|
||||
return true
|
||||
}
|
||||
|
||||
# the LLVM bin directory to split with, or null (Windows' shell cannot run the parts at once yet)
|
||||
function split_dir() -> pointer {
|
||||
if host_is_windows() { return null }
|
||||
if getenv_or("LUDIC_SPLIT", "1") == "0" { return null }
|
||||
let d = getenv_or("LUDIC_LLVM", "/opt/homebrew/opt/llvm/bin")
|
||||
if not split_there(`{d}/llvm-split`) or not split_there(`{d}/clang`) { return null }
|
||||
return d
|
||||
}
|
||||
|
||||
# how many parts: $LUDIC_JOBS, else the cores, no more than the free memory allows at 1.5 GB a clang
|
||||
function split_jobs(tmp: pointer) -> int {
|
||||
let j = getenv("LUDIC_JOBS")
|
||||
if j != null { return split_int(j) }
|
||||
let probe = `{tmp}.cores`
|
||||
let aw = "awk '/Pages free/{f=$3} /Pages inactive/{i=$3} END{print int((f+i)*p/1073741824)}'"
|
||||
run(`echo $(sysctl -n hw.ncpu) $(vm_stat | {aw} p=$(sysctl -n hw.pagesize)) > "{probe}" 2>/dev/null`)
|
||||
let f = file_open(probe, "rb")
|
||||
if f == null { return 1 }
|
||||
let buf = bytes(64)
|
||||
let n = file_read(f, buf, 63)
|
||||
file_close(f)
|
||||
remove_file(`"{probe}"`)
|
||||
if n <= 0 { return 1 }
|
||||
buf[n] = 0
|
||||
var cores = 0
|
||||
var i = 0
|
||||
while i < n and buf[i] >= '0' and buf[i] <= '9' { cores = cores * 10 + (buf[i] - '0'); i += 1 }
|
||||
while i < n and buf[i] == ' ' { i += 1 }
|
||||
var gb = 0
|
||||
while i < n and buf[i] >= '0' and buf[i] <= '9' { gb = gb * 10 + (buf[i] - '0'); i += 1 }
|
||||
let by_mem = (gb * 2) / 3
|
||||
if by_mem < cores { return by_mem }
|
||||
return cores
|
||||
}
|
||||
function split_int(s: pointer) -> int {
|
||||
var v = 0
|
||||
var i = 0
|
||||
while i < len(s) and s[i] >= '0' and s[i] <= '9' { v = v * 10 + (s[i] - '0'); i += 1 }
|
||||
return v
|
||||
}
|
||||
|
||||
# the IR at `ll` compiled in parts at -O`opt`: their objects, quoted and space-separated, for the
|
||||
# link to take in place of the .ll; null when it was not split (the caller compiles the .ll whole)
|
||||
function split_compile(ll: pointer, out: pointer, opt: pointer) -> pointer {
|
||||
let d = split_dir()
|
||||
if d == null { return null }
|
||||
let n = split_jobs(out)
|
||||
if n < 2 { return null }
|
||||
let pre = `{out}.part`
|
||||
if run(`"{d}/llvm-split" -j {n} -o "{pre}" "{ll}"`) != 0 { return null }
|
||||
var sh = ""
|
||||
var i = 0
|
||||
while i < n {
|
||||
sh = sh + `"{d}/clang" -x ir -O{opt} -mmacosx-version-min=11.0 -Wno-override-module -Wno-missing-sysroot -c "{pre}{itoa(i)}" -o "{pre}{itoa(i)}.o" & `
|
||||
i += 1
|
||||
}
|
||||
run(`/bin/sh -c '{sh}wait'`)
|
||||
var objs = ""
|
||||
var ok = true
|
||||
i = 0
|
||||
while i < n {
|
||||
remove_file(`"{pre}{itoa(i)}"`)
|
||||
if not split_there(`{pre}{itoa(i)}.o`) { ok = false }
|
||||
objs = objs + ` "{pre}{itoa(i)}.o"`
|
||||
i += 1
|
||||
}
|
||||
if not ok {
|
||||
split_clean(out)
|
||||
return null
|
||||
}
|
||||
return objs
|
||||
}
|
||||
# the parts' objects, once linked (or when a part failed)
|
||||
function split_clean(out: pointer) -> void { run(`rm -f "{out}".part*.o`) }
|
||||
|
|
@ -54,7 +54,12 @@ function compile_app(src: pointer, out: pointer, mode: int, save: bool) -> bool
|
|||
|
||||
if mode == 2 {
|
||||
if not shq(`{ludicc()} --headless{title_flag()}{unsafe_flag()} {src} --emit-llvm -o {ll}`) { return false }
|
||||
if not shq(`{cc()} -O2 {ll}{gl_link_flags(ll)}{vk_link_flags(ll)}{http_link_flags(ll)}{udp_link_flags(ll)}{process_link_flags(ll)}{threads_link_flags(ll)}{native_link_flags(ll)}{pbf} -o {out}`) { return false }
|
||||
let objs = split_objs(ll, out) # on every core when it can (split.ludic)
|
||||
var input = ll
|
||||
if objs != "" { input = objs }
|
||||
let linked = shq(`{cc()} -O{split_opt()} {input}{gl_link_flags(ll)}{vk_link_flags(ll)}{http_link_flags(ll)}{udp_link_flags(ll)}{process_link_flags(ll)}{threads_link_flags(ll)}{native_link_flags(ll)}{pbf} -o {out}`)
|
||||
if objs != "" { shell(`rm -f {objs}`) }
|
||||
if not linked { return false }
|
||||
g_built_natives = native_libs_of(ll)
|
||||
if not save { shell(`rm -f {ll}`) }
|
||||
return true
|
||||
|
|
@ -65,7 +70,12 @@ function compile_app(src: pointer, out: pointer, mode: int, save: bool) -> bool
|
|||
# canonical `ludicc -o` path links it only when Audio.* is used.
|
||||
let cocoa = `{home}runtime/native/cocoa.ll`
|
||||
let audio = `{home}runtime/native/audio.ll`
|
||||
if not shq(`{cc()} -O2 {ll} {cocoa} {audio} -framework Cocoa -Wl,-needed_framework,GameController -Wl,-needed_framework,AVFoundation -Wl,-rpath,@loader_path{gl_link_flags(ll)}{vk_link_flags(ll)}{http_link_flags(ll)}{udp_link_flags(ll)}{process_link_flags(ll)}{threads_link_flags(ll)}{native_link_flags(ll)}{pbf} -o {out}`) { return false }
|
||||
let objs = split_objs(ll, out)
|
||||
var input = ll
|
||||
if objs != "" { input = objs }
|
||||
let linked = shq(`{cc()} -O{split_opt()} {input} {cocoa} {audio} -framework Cocoa -Wl,-needed_framework,GameController -Wl,-needed_framework,AVFoundation -Wl,-rpath,@loader_path{gl_link_flags(ll)}{vk_link_flags(ll)}{http_link_flags(ll)}{udp_link_flags(ll)}{process_link_flags(ll)}{threads_link_flags(ll)}{native_link_flags(ll)}{pbf} -o {out}`)
|
||||
if objs != "" { shell(`rm -f {objs}`) }
|
||||
if not linked { return false }
|
||||
g_built_natives = native_libs_of(ll)
|
||||
if not save { shell(`rm -f {ll}`) }
|
||||
return true
|
||||
|
|
|
|||
|
|
@ -357,6 +357,7 @@ function cmd_bundle_windows() -> int {
|
|||
let staged = tmp_path("bundle_exe")
|
||||
var flags = "--windowed --gui"
|
||||
if res != "" { flags = flags + ` --link "{res}"` }
|
||||
Os.set_env("LUDIC_OPT", "2") # a shipped game is -O2, whatever a developer's shell says
|
||||
if not shq(`{ludicc()} {flags}{title_flag()} {entry} --natives-out {staged}.natives -o {staged}`) { return 1 }
|
||||
if not shq(`cp "{staged}.exe" "{root}/{name}.exe"`) {
|
||||
err("ludic bundle: could not place the executable\n")
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@
|
|||
program LudicDev {
|
||||
import "prelude.ludic"
|
||||
import "build.ludic"
|
||||
import "split.ludic"
|
||||
import "toolchain.ludic"
|
||||
import "selfhost.ludic"
|
||||
import "tools.ludic"
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@
|
|||
program Ludic {
|
||||
import "prelude.ludic"
|
||||
import "build.ludic"
|
||||
import "split.ludic"
|
||||
import "project.ludic"
|
||||
import "pkg.ludic"
|
||||
import "assets.ludic"
|
||||
|
|
|
|||
|
|
@ -109,6 +109,7 @@ function selfhost_frags() -> []pointer {
|
|||
push(f, "selfhost/backend/stdlib/emit_net.ludic")
|
||||
push(f, "selfhost/backend/game/emit_ui.ludic")
|
||||
push(f, "selfhost/backend/emit_decl.ludic")
|
||||
push(f, "selfhost/split_build.ludic")
|
||||
push(f, "selfhost/main.ludic")
|
||||
return f
|
||||
}
|
||||
|
|
|
|||
49
tools/ludic-cli/split.ludic
Normal file
49
tools/ludic-cli/split.ludic
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
# split.ludic - `ludic build` on every core: the program's IR cut into N parts (llvm-split), a clang
|
||||
# each at once, their objects linked where the one .ll was (the game: clang was 32 s of a 41 s
|
||||
# build, on one core). ludicc's own link does the same (selfhost/split_build.ludic). It needs an
|
||||
# llvm-split and a clang of one LLVM - $LUDIC_LLVM, else Homebrew's - or it compiles the .ll whole.
|
||||
|
||||
function split_opt() -> pointer { return getenv_or("LUDIC_OPT", "2") } # 1: a developer's faster build
|
||||
|
||||
# " a.o b.o ..." for the link, or "" when not split (the caller compiles the .ll as before)
|
||||
function split_objs(ll: pointer, out: pointer) -> pointer {
|
||||
if host_windows() or getenv_or("LUDIC_SPLIT", "1") == "0" { return "" }
|
||||
let d = getenv_or("LUDIC_LLVM", "/opt/homebrew/opt/llvm/bin")
|
||||
if not is_exec(`"{d}/llvm-split"`) or not is_exec(`"{d}/clang"`) { return "" }
|
||||
let n = split_n()
|
||||
if n < 2 { return "" }
|
||||
let pre = tmp_path(`{file_of_path(out)}.part`)
|
||||
if not shq(`"{d}/llvm-split" -j {n} -o "{pre}" "{ll}"`) { return "" }
|
||||
var sh = ""
|
||||
for i in 0 .. n {
|
||||
sh = sh + `"{d}/clang" -x ir -O{split_opt()} -mmacosx-version-min=11.0 -Wno-override-module -Wno-missing-sysroot -c "{pre}{i}" -o "{pre}{i}.o" & `
|
||||
}
|
||||
shell(`{sh}wait`)
|
||||
var objs = ""
|
||||
var ok = true
|
||||
for i in 0 .. n {
|
||||
shell(`rm -f "{pre}{i}"`)
|
||||
if not file_exists(`"{pre}{i}.o"`) { ok = false }
|
||||
objs = objs + ` "{pre}{i}.o"`
|
||||
}
|
||||
if ok { return objs }
|
||||
shell(`rm -f {objs}`)
|
||||
return ""
|
||||
}
|
||||
|
||||
# $LUDIC_JOBS, else the cores - no more than the free memory allows at 1.5 GB a clang
|
||||
function split_n() -> int {
|
||||
let j = getenv("LUDIC_JOBS")
|
||||
if j != null { return s_to_int(j) }
|
||||
let aw = "awk '/Pages free/{f=$3} /Pages inactive/{i=$3} END{print int((f+i)*p/1073741824)}'"
|
||||
let line = capture_line(`echo $(sysctl -n hw.ncpu) $(vm_stat | {aw} p=$(sysctl -n hw.pagesize))`)
|
||||
var cores = 0
|
||||
var i = 0
|
||||
while i < len(line) and line[i] >= '0' and line[i] <= '9' { cores = cores * 10 + (line[i] - '0'); i += 1 }
|
||||
while i < len(line) and line[i] == ' ' { i += 1 }
|
||||
var gb = 0
|
||||
while i < len(line) and line[i] >= '0' and line[i] <= '9' { gb = gb * 10 + (line[i] - '0'); i += 1 }
|
||||
let by_mem = (gb * 2) / 3
|
||||
if by_mem < cores { return by_mem }
|
||||
return cores
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue