build: refactor emitStdioNobuf for performance and readability

This commit is contained in:
Li Jie
2025-11-16 19:07:34 +08:00
parent 1473ee98f7
commit 7e4e53eb1c
2 changed files with 30 additions and 29 deletions

View File

@@ -159,3 +159,8 @@ func runBinary(t *testing.T, path string, args ...string) string {
} }
return string(output) return string(output)
} }
func TestRunPrintfWithStdioNobuf(t *testing.T) {
t.Setenv(llgoStdioNobuf, "1")
mockRun([]string{"../../cl/_testdata/printf"}, &Config{Mode: ModeRun})
}

View File

@@ -118,7 +118,7 @@ func defineEntryFunction(ctx *context, pkg llssa.Package, argcVar, argvVar llssa
b.Store(argcVar.Expr, fn.Param(0)) b.Store(argcVar.Expr, fn.Param(0))
b.Store(argvVar.Expr, fn.Param(1)) b.Store(argvVar.Expr, fn.Param(1))
if IsStdioNobuf() { if IsStdioNobuf() {
emitStdioNobuf(b, pkg, ctx.buildConf.Goarch) emitStdioNobuf(b, pkg, ctx.buildConf.Goos)
} }
if pyInit != nil { if pyInit != nil {
b.Call(pyInit.Expr) b.Call(pyInit.Expr)
@@ -154,47 +154,43 @@ func defineWeakNoArgStub(pkg llssa.Package, name string) llssa.Function {
return fn return fn
} }
func emitStdioNobuf(b llssa.Builder, pkg llssa.Package, goarch string) { const (
// ioNoBuf represents the _IONBF flag for setvbuf (no buffering)
ioNoBuf = 2
)
// emitStdioNobuf generates code to disable buffering on stdout and stderr // emitStdioNobuf generates code to disable buffering on stdout and stderr
// when the LLGO_STDIO_NOBUF environment variable is set. Each stream is // when the LLGO_STDIO_NOBUF environment variable is set. Only Darwin uses
// checked independently so missing stdio symbols are handled gracefully. // the alternate `__stdoutp`/`__stderrp` symbols; other targets rely on the
// standard `stdout`/`stderr` globals.
func emitStdioNobuf(b llssa.Builder, pkg llssa.Package, goos string) {
prog := pkg.Prog prog := pkg.Prog
streamType := prog.VoidPtr() streamType := prog.VoidPtr()
streamPtrType := prog.Pointer(streamType) streamPtrType := prog.Pointer(streamType)
stdout := declareExternalPtrGlobal(pkg, "stdout", streamType)
stderr := declareExternalPtrGlobal(pkg, "stderr", streamType) stdoutName := "stdout"
stdoutAlt := declareExternalPtrGlobal(pkg, "__stdout", streamType) stderrName := "stderr"
stderrAlt := declareExternalPtrGlobal(pkg, "__stderr", streamType) if goos == "darwin" {
stdoutName = "__stdoutp"
stderrName = "__stderrp"
}
stdout := declareExternalPtrGlobal(pkg, stdoutName, streamPtrType)
stderr := declareExternalPtrGlobal(pkg, stderrName, streamPtrType)
stdoutPtr := b.Load(stdout)
stderrPtr := b.Load(stderr)
sizeType := prog.Uintptr() sizeType := prog.Uintptr()
setvbuf := declareSetvbuf(pkg, streamPtrType, prog.CStr(), prog.Int32(), sizeType) setvbuf := declareSetvbuf(pkg, streamPtrType, prog.CStr(), prog.Int32(), sizeType)
stdoutSlot := b.AllocaT(streamPtrType) noBufMode := prog.IntVal(ioNoBuf, prog.Int32())
b.Store(stdoutSlot, stdout)
condOut := b.BinOp(token.EQL, stdout, prog.Nil(streamPtrType))
b.IfThen(condOut, func() {
b.Store(stdoutSlot, stdoutAlt)
})
stdoutPtr := b.Load(stdoutSlot)
stderrSlot := b.AllocaT(streamPtrType)
b.Store(stderrSlot, stderr)
condErr := b.BinOp(token.EQL, stderr, prog.Nil(streamPtrType))
b.IfThen(condErr, func() {
b.Store(stderrSlot, stderrAlt)
})
stderrPtr := b.Load(stderrSlot)
mode := prog.IntVal(2, prog.Int32())
zeroSize := prog.Zero(sizeType) zeroSize := prog.Zero(sizeType)
nullBuf := prog.Nil(prog.CStr()) nullBuf := prog.Nil(prog.CStr())
b.Call(setvbuf.Expr, stdoutPtr, nullBuf, mode, zeroSize) b.Call(setvbuf.Expr, stdoutPtr, nullBuf, noBufMode, zeroSize)
b.Call(setvbuf.Expr, stderrPtr, nullBuf, mode, zeroSize) b.Call(setvbuf.Expr, stderrPtr, nullBuf, noBufMode, zeroSize)
} }
func declareExternalPtrGlobal(pkg llssa.Package, name string, valueType llssa.Type) llssa.Expr { func declareExternalPtrGlobal(pkg llssa.Package, name string, valueType llssa.Type) llssa.Expr {
ptrType := pkg.Prog.Pointer(valueType) global := pkg.NewVarEx(name, valueType)
global := pkg.NewVarEx(name, ptrType)
pkg.Module().NamedGlobal(name).SetLinkage(llvm.ExternalLinkage) pkg.Module().NamedGlobal(name).SetLinkage(llvm.ExternalLinkage)
return global.Expr return global.Expr
} }