Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions chore/litgen/litgen_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package main
import (
"os"
"path/filepath"
"regexp"
"strings"
"testing"

Expand Down Expand Up @@ -46,8 +47,8 @@ func TestProcessPath_SingleFileUsesContainingDir(t *testing.T) {
if err != nil {
t.Fatal(err)
}
want := `// CHECK-LABEL: define void @"{{.*}}/` + filepath.ToSlash(relPath) + `.main"() {`
if !strings.Contains(text, want) {
want := regexp.MustCompile(`(?m)^// CHECK-LABEL: define void @"\{\{.*\}\}/` + regexp.QuoteMeta(filepath.ToSlash(relPath)) + `\.main"\(\)(?: #[0-9]+)?(?: !dbg ![0-9]+)? \{$`)
if !want.MatchString(text) {
t.Fatalf("missing package-qualified main check:\n%s", text)
}
}
2 changes: 1 addition & 1 deletion chore/litgen/rewrite.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ var (
globalPlainRE = regexp.MustCompile(`^@([A-Za-z0-9$._-]+)\s*=`)
globalRefRE = regexp.MustCompile(`@"([^"]+)"|@([A-Za-z0-9$._-]+)`)
checkLineRE = regexp.MustCompile(`^\s*//\s*CHECK(?:-[A-Z]+)?:`)
debugMetaRE = regexp.MustCompile(`, ![A-Za-z0-9_.-]+ ![0-9]+`)
debugMetaRE = regexp.MustCompile(`(?:,\s*|\s+)![A-Za-z0-9_.-]+ ![0-9]+`)
attrGroupTailRE = regexp.MustCompile(`\s+#\d+$`)
numericNameRE = regexp.MustCompile(`^\d+$`)
)
Expand Down
35 changes: 33 additions & 2 deletions cl/cltest/cltest.go
Original file line number Diff line number Diff line change
Expand Up @@ -208,7 +208,10 @@ func testFrom(t *testing.T, pkgDir, sel string) {
if spec.Mode == littest.ModeSkip {
return
}
v := llgen.GenFrom(pkgDir)
var v string
withLineInfoDisabled(func() {
v = llgen.GenFrom(pkgDir)
})
if spec.Mode == littest.ModeFileCheck {
if err := littest.Check(spec, v); err != nil {
_ = os.WriteFile(pkgDir+"/result.txt", []byte(v), 0644)
Expand Down Expand Up @@ -260,7 +263,14 @@ func testRunAndTestFrom(t *testing.T, pkgDir, relPkg, sel string, opts runOption
}
}

output, err := runWithConf(relPkg, pkgDir, conf)
var output []byte
if checkIR {
withLineInfoDisabled(func() {
output, err = runWithConf(relPkg, pkgDir, conf)
})
} else {
output, err = runWithConf(relPkg, pkgDir, conf)
}
if err != nil {
t.Logf("raw output:\n%s", string(output))
t.Fatalf("run failed: %v\noutput: %s", err, string(output))
Expand Down Expand Up @@ -427,6 +437,20 @@ func readIRSpec(pkgDir string) (littest.Spec, bool, error) {
return spec, true, nil
}

func withLineInfoDisabled(fn func()) {
const key = "LLGO_LINEINFO"
old, ok := os.LookupEnv(key)
_ = os.Setenv(key, "0")
defer func() {
if ok {
_ = os.Setenv(key, old)
} else {
_ = os.Unsetenv(key)
}
}()
fn()
}

func filterRunOutput(in []byte) []byte {
// Tests compare output with expect.txt. Some toolchain/environment warnings are
// inherently machine-specific and should not be part of the golden output.
Expand Down Expand Up @@ -460,6 +484,13 @@ func filterRunOutput(in []byte) []byte {

func TestCompileEx(t *testing.T, src any, fname, expected string, dbg bool) {
t.Helper()
// Build.Do configures cl debug globals for full-package builds. Keep the
// single-file compiler assertions independent from any prior build test.
cl.EnableDebug(dbg)
cl.EnableDbgSyms(dbg)
defer cl.EnableDebug(false)
defer cl.EnableDbgSyms(false)

fset := token.NewFileSet()
f, err := parser.ParseFile(fset, fname, src, parser.ParseComments)
if err != nil {
Expand Down
54 changes: 53 additions & 1 deletion cl/compile.go
Original file line number Diff line number Diff line change
Expand Up @@ -486,6 +486,9 @@ func (p *context) compileFuncDecl(pkg llssa.Package, f *ssa.Function) (llssa.Fun
for _, childInit := range childInits {
childInit()
}
if dbgEnabled {
b.DISetCurrentDebugLocation(p.fn, p.getFuncEndPos(f))
}
b.EndBuild()
})
}
Expand All @@ -501,6 +504,18 @@ func (p *context) getFuncBodyPos(f *ssa.Function) token.Position {
return p.goProg.Fset.Position(f.Pos())
}

func (p *context) getFuncEndPos(f *ssa.Function) token.Position {
if syntax := f.Syntax(); syntax != nil && syntax.End().IsValid() {
return p.goProg.Fset.Position(syntax.End())
}
if f.Object() != nil {
if fn, ok := f.Object().(*types.Func); ok && fn.Scope() != nil && fn.Scope().End().IsValid() {
return p.goProg.Fset.Position(fn.Scope().End())
}
}
return p.getFuncBodyPos(f)
}

func isGlobal(v *types.Var) bool {
// TODO(lijie): better implementation
return strings.HasPrefix(v.Parent().String(), "package ")
Expand Down Expand Up @@ -865,7 +880,7 @@ func (p *context) compileInstrOrValue(b llssa.Builder, iv instrOrValue, asValue
ret = b.BinOp(v.Op, x, y)
case *ssa.UnOp:
if v.Op == token.MUL {
if refs := v.Referrers(); refs != nil && len(*refs) == 0 {
if hasNoSemanticReferrers(v) {
if t := p.type_(v.Type(), llssa.InGo); t.RawType() != nil {
if p.isLargeNonPointerValue(t) {
x := p.compileValue(b, v.X)
Expand Down Expand Up @@ -1092,6 +1107,9 @@ func (p *context) getDebugLocScope(v *ssa.Function, pos token.Pos) *types.Scope

func (p *context) compileInstr(b llssa.Builder, instr ssa.Instruction) {
if iv, ok := instr.(instrOrValue); ok {
if !enableDbgSyms && debugOnlyPureValue(iv) {
return
}
p.compileInstrOrValue(b, iv, false)
return
}
Expand Down Expand Up @@ -1139,6 +1157,9 @@ func (p *context) compileInstr(b llssa.Builder, instr ssa.Instruction) {
}
}
if p.returnNeedsImplicitRunDefers(v) {
if enableDbg {
b.DISetCurrentDebugLocation(p.fn, p.getFuncEndPos(v.Parent()))
}
b.RunDefers()
}
b.Return(results...)
Expand Down Expand Up @@ -1180,6 +1201,37 @@ func (p *context) compileInstr(b llssa.Builder, instr ssa.Instruction) {
}
}

func hasNoSemanticReferrers(v ssa.Value) bool {
refs := v.Referrers()
if refs == nil || len(*refs) == 0 {
return true
}
for _, ref := range *refs {
if _, ok := ref.(*ssa.DebugRef); !ok {
return false
}
}
return true
}

func debugOnlyPureValue(v ssa.Value) bool {
refs := v.Referrers()
if refs == nil || len(*refs) == 0 {
return false
}
for _, ref := range *refs {
if _, ok := ref.(*ssa.DebugRef); !ok {
return false
}
}
switch v.(type) {
case *ssa.Extract, *ssa.ChangeType, *ssa.Convert, *ssa.ChangeInterface, *ssa.MakeInterface:
return true
default:
return false
}
}

func (p *context) getLocalVariable(b llssa.Builder, fn *ssa.Function, v *types.Var) llssa.DIVar {
if p.paramDIVars != nil {
if div, ok := p.paramDIVars[v]; ok {
Expand Down
48 changes: 42 additions & 6 deletions internal/build/build.go
Original file line number Diff line number Diff line change
Expand Up @@ -278,7 +278,7 @@ func Do(args []string, conf *Config) ([]Package, error) {
cfg.Mode |= packages.NeedForTest
}

cl.EnableDebug(IsDbgEnabled())
cl.EnableDebug(IsLineInfoEnabled())
cl.EnableDbgSyms(IsDbgSymsEnabled())
cl.EnableTrace(IsTraceEnabled())
llssa.Initialize(llssa.InitAll)
Expand Down Expand Up @@ -367,11 +367,13 @@ func Do(args []string, conf *Config) ([]Package, error) {
buildMode := ssaBuildMode
cabiOptimize := true
passOpt := true
if IsDbgEnabled() || mode == ModeGen {
if IsLineInfoEnabled() || mode == ModeGen {
passOpt = false
}
if IsDbgEnabled() {
if IsLineInfoEnabled() {
buildMode |= ssa.GlobalDebug
}
if IsDbgEnabled() {
cabiOptimize = false
}
if !IsOptimizeEnabled() {
Expand Down Expand Up @@ -1105,8 +1107,8 @@ func linkObjFiles(ctx *context, app string, objFiles, linkArgs []string, verbose
}
}

// Add common linker arguments based on target OS and architecture
if IsDbgSymsEnabled() {
// Add common linker arguments based on target OS and architecture.
if useDWARFLinkFlag(ctx) {
buildArgs = append(buildArgs, "-gdwarf-4")
}

Expand Down Expand Up @@ -1134,7 +1136,36 @@ func linkObjFiles(ctx *context, app string, objFiles, linkArgs []string, verbose

cmd := ctx.linker()
cmd.Verbose = printCmds
return cmd.Link(buildArgs...)
if err := cmd.Link(buildArgs...); err != nil {
return err
}
return emitDarwinDSYM(ctx, app, printCmds)
}

func useDWARFLinkFlag(ctx *context) bool {
if !IsLineInfoEnabled() && !IsDbgSymsEnabled() {
return false
}
// -g/-gdwarf flags are driver options. Bare linker frontends such as
// ld.lld and wasm-ld reject them.
return ctx.crossCompile.Linker == ""
}

func emitDarwinDSYM(ctx *context, app string, verbose bool) error {
if !IsLineInfoEnabled() || ctx.buildConf.Goos != "darwin" || runtime.GOOS != "darwin" || ctx.buildConf.Target != "" {
return nil
}
if _, err := exec.LookPath("dsymutil"); err != nil {
return nil
}
args := []string{"-f", "-o", app + ".dSYM", app}
if verbose {
fmt.Fprintf(os.Stderr, "dsymutil %s\n", strings.Join(args, " "))
}
cmd := exec.Command("dsymutil", args...)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
return cmd.Run()
}

func needsLinuxNoPIE(ctx *context, linkArgs []string) bool {
Expand Down Expand Up @@ -1753,6 +1784,7 @@ var (

const llgoDebug = "LLGO_DEBUG"
const llgoDbgSyms = "LLGO_DEBUG_SYMBOLS"
const llgoLineInfo = "LLGO_LINEINFO"
const llgoTrace = "LLGO_TRACE"
const llgoOptimize = "LLGO_OPTIMIZE"
const llgoWasmRuntime = "LLGO_WASM_RUNTIME"
Expand Down Expand Up @@ -1800,6 +1832,10 @@ func IsDbgEnabled() bool {
return isEnvOn(llgoDebug, false) || isEnvOn(llgoDbgSyms, false)
}

func IsLineInfoEnabled() bool {
return isEnvOn(llgoLineInfo, true) || IsDbgEnabled()
}

func IsDbgSymsEnabled() bool {
return isEnvOn(llgoDbgSyms, false)
}
Expand Down
24 changes: 24 additions & 0 deletions internal/build/build_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,30 @@ func TestNeedsLinuxNoPIE(t *testing.T) {
}
}

func TestUseDWARFLinkFlag(t *testing.T) {
t.Setenv(llgoLineInfo, "1")
t.Setenv(llgoDebug, "0")
t.Setenv(llgoDbgSyms, "0")

ctx := &context{}
if !useDWARFLinkFlag(ctx) {
t.Fatal("clang driver link should accept -gdwarf-4 when line info is enabled")
}

for _, linker := range []string{"ld.lld", "wasm-ld"} {
ctx.crossCompile.Linker = linker
if useDWARFLinkFlag(ctx) {
t.Fatalf("%s should not receive clang driver debug flags", linker)
}
}

t.Setenv(llgoLineInfo, "0")
ctx.crossCompile.Linker = ""
if useDWARFLinkFlag(ctx) {
t.Fatal("disabled line info should not add -gdwarf-4")
}
}

func mockRun(args []string, cfg *Config) {
defer mockable.DisableMock()
mockable.EnableMock()
Expand Down
1 change: 1 addition & 0 deletions internal/build/collect.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ func (c *context) collectEnvInputs(m *manifestBuilder) {
envVars := []string{
llgoDebug,
llgoDbgSyms,
llgoLineInfo,
llgoTrace,
llgoOptimize,
llgoWasmRuntime,
Expand Down
Loading