1//===- debug.go - debug info builder --------------------------------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8//
9// This package builds LLVM debug info from go/* data structures.
10//
11//===----------------------------------------------------------------------===//
12
13package debug
14
15import (
16	"debug/dwarf"
17	"fmt"
18	"go/token"
19	"os"
20	"strings"
21
22	"llvm.org/llgo/third_party/gotools/go/ssa"
23	"llvm.org/llgo/third_party/gotools/go/types"
24	"llvm.org/llgo/third_party/gotools/go/types/typeutil"
25
26	"llvm.org/llvm/bindings/go/llvm"
27)
28
29const (
30	// non-standard debug metadata tags
31	tagAutoVariable dwarf.Tag = 0x100
32	tagArgVariable  dwarf.Tag = 0x101
33)
34
35type PrefixMap struct {
36	Source, Replacement string
37}
38
39// DIBuilder builds debug metadata for Go programs.
40type DIBuilder struct {
41	// builder is the current builder; there is one per CU.
42	builder    *llvm.DIBuilder
43	module     llvm.Module
44	files      map[*token.File]llvm.Metadata
45	cu, fn, lb llvm.Metadata
46	fnFile     string
47	sizes      types.Sizes
48	fset       *token.FileSet
49	prefixMaps []PrefixMap
50	types      typeutil.Map
51	voidType   llvm.Metadata
52}
53
54// NewDIBuilder creates a new debug information builder.
55func NewDIBuilder(sizes types.Sizes, module llvm.Module, fset *token.FileSet, prefixMaps []PrefixMap) *DIBuilder {
56	var d DIBuilder
57	d.module = module
58	d.files = make(map[*token.File]llvm.Metadata)
59	d.sizes = sizes
60	d.fset = fset
61	d.prefixMaps = prefixMaps
62	d.builder = llvm.NewDIBuilder(d.module)
63	d.cu = d.createCompileUnit()
64	return &d
65}
66
67// Destroy destroys the DIBuilder.
68func (d *DIBuilder) Destroy() {
69	d.builder.Destroy()
70}
71
72func (d *DIBuilder) scope() llvm.Metadata {
73	if d.lb.C != nil {
74		return d.lb
75	}
76	if d.fn.C != nil {
77		return d.fn
78	}
79	return d.cu
80}
81
82func (d *DIBuilder) remapFilePath(path string) string {
83	for _, pm := range d.prefixMaps {
84		if strings.HasPrefix(path, pm.Source) {
85			return pm.Replacement + path[len(pm.Source):]
86		}
87	}
88	return path
89}
90
91func (d *DIBuilder) getFile(file *token.File) llvm.Metadata {
92	if diFile := d.files[file]; diFile.C != nil {
93		return diFile
94	}
95	diFile := d.builder.CreateFile(d.remapFilePath(file.Name()), "")
96	d.files[file] = diFile
97	return diFile
98}
99
100// createCompileUnit creates and returns debug metadata for the compile
101// unit as a whole, using the first file in the file set as a representative
102// (the choice of file is arbitrary).
103func (d *DIBuilder) createCompileUnit() llvm.Metadata {
104	var file *token.File
105	d.fset.Iterate(func(f *token.File) bool {
106		file = f
107		return false
108	})
109	dir, err := os.Getwd()
110	if err != nil {
111		panic("could not get current directory: " + err.Error())
112	}
113	return d.builder.CreateCompileUnit(llvm.DICompileUnit{
114		Language: llvm.DW_LANG_Go,
115		File:     d.remapFilePath(file.Name()),
116		Dir:      dir,
117		Producer: "llgo",
118	})
119}
120
121// PushFunction creates debug metadata for the specified function,
122// and pushes it onto the scope stack.
123func (d *DIBuilder) PushFunction(fnptr llvm.Value, sig *types.Signature, pos token.Pos) {
124	var diFile llvm.Metadata
125	var line int
126	if file := d.fset.File(pos); file != nil {
127		d.fnFile = file.Name()
128		diFile = d.getFile(file)
129		line = file.Line(pos)
130	}
131	d.fn = d.builder.CreateFunction(d.scope(), llvm.DIFunction{
132		Name:         fnptr.Name(), // TODO(axw) unmangled name?
133		LinkageName:  fnptr.Name(),
134		File:         diFile,
135		Line:         line,
136		Type:         d.DIType(sig),
137		IsDefinition: true,
138	})
139	fnptr.SetSubprogram(d.fn)
140}
141
142// PopFunction pops the previously pushed function off the scope stack.
143func (d *DIBuilder) PopFunction() {
144	d.lb = llvm.Metadata{}
145	d.fn = llvm.Metadata{}
146	d.fnFile = ""
147}
148
149// Value creates an llvm.dbg.value call for the specified register value.
150func (d *DIBuilder) Value(b llvm.Builder, v ssa.Value, llv llvm.Value, paramIndex int) {
151	// TODO(axw)
152}
153
154// SetLocation sets the current debug location.
155func (d *DIBuilder) SetLocation(b llvm.Builder, pos token.Pos) {
156	position := d.fset.Position(pos)
157	d.lb = llvm.Metadata{}
158	if position.Filename != d.fnFile && position.Filename != "" {
159		// This can happen rarely, e.g. in init functions.
160		diFile := d.builder.CreateFile(d.remapFilePath(position.Filename), "")
161		d.lb = d.builder.CreateLexicalBlockFile(d.scope(), diFile, 0)
162	}
163	b.SetCurrentDebugLocation(uint(position.Line), uint(position.Column), d.scope(), llvm.Metadata{})
164}
165
166// Finalize must be called after all compilation units are translated,
167// generating the final debug metadata for the module.
168func (d *DIBuilder) Finalize() {
169	d.module.AddNamedMetadataOperand(
170		"llvm.module.flags",
171		llvm.GlobalContext().MDNode([]llvm.Metadata{
172			llvm.ConstInt(llvm.Int32Type(), 2, false).ConstantAsMetadata(), // Warn on mismatch
173			llvm.GlobalContext().MDString("Dwarf Version"),
174			llvm.ConstInt(llvm.Int32Type(), 4, false).ConstantAsMetadata(),
175		}),
176	)
177	d.module.AddNamedMetadataOperand(
178		"llvm.module.flags",
179		llvm.GlobalContext().MDNode([]llvm.Metadata{
180			llvm.ConstInt(llvm.Int32Type(), 1, false).ConstantAsMetadata(), // Error on mismatch
181			llvm.GlobalContext().MDString("Debug Info Version"),
182			llvm.ConstInt(llvm.Int32Type(), 3, false).ConstantAsMetadata(),
183		}),
184	)
185	d.builder.Finalize()
186}
187
188// DIType maps a Go type to DIType debug metadata value.
189func (d *DIBuilder) DIType(t types.Type) llvm.Metadata {
190	return d.typeDebugDescriptor(t, types.TypeString(nil, t))
191}
192
193func (d *DIBuilder) typeDebugDescriptor(t types.Type, name string) llvm.Metadata {
194	// Signature needs to be handled specially, to preprocess
195	// methods, moving the receiver to the parameter list.
196	if t, ok := t.(*types.Signature); ok {
197		return d.descriptorSignature(t, name)
198	}
199	if t == nil {
200		if d.voidType.C == nil {
201			d.voidType = d.builder.CreateBasicType(llvm.DIBasicType{Name: "void"})
202		}
203		return d.voidType
204	}
205	if dt, ok := d.types.At(t).(llvm.Metadata); ok {
206		return dt
207	}
208	dt := d.descriptor(t, name)
209	d.types.Set(t, dt)
210	return dt
211}
212
213func (d *DIBuilder) descriptor(t types.Type, name string) llvm.Metadata {
214	switch t := t.(type) {
215	case *types.Basic:
216		return d.descriptorBasic(t, name)
217	case *types.Pointer:
218		return d.descriptorPointer(t)
219	case *types.Struct:
220		return d.descriptorStruct(t, name)
221	case *types.Named:
222		return d.descriptorNamed(t)
223	case *types.Array:
224		return d.descriptorArray(t, name)
225	case *types.Slice:
226		return d.descriptorSlice(t, name)
227	case *types.Map:
228		return d.descriptorMap(t, name)
229	case *types.Chan:
230		return d.descriptorChan(t, name)
231	case *types.Interface:
232		return d.descriptorInterface(t, name)
233	default:
234		panic(fmt.Sprintf("unhandled type: %T", t))
235	}
236}
237
238func (d *DIBuilder) descriptorBasic(t *types.Basic, name string) llvm.Metadata {
239	switch t.Kind() {
240	case types.String:
241		return d.typeDebugDescriptor(types.NewStruct([]*types.Var{
242			types.NewVar(0, nil, "ptr", types.NewPointer(types.Typ[types.Uint8])),
243			types.NewVar(0, nil, "len", types.Typ[types.Int]),
244		}, nil), name)
245	case types.UnsafePointer:
246		return d.builder.CreateBasicType(llvm.DIBasicType{
247			Name:        name,
248			SizeInBits:  uint64(d.sizes.Sizeof(t) * 8),
249			Encoding:    llvm.DW_ATE_unsigned,
250		})
251	default:
252		bt := llvm.DIBasicType{
253			Name:        t.String(),
254			SizeInBits:  uint64(d.sizes.Sizeof(t) * 8),
255		}
256		switch bi := t.Info(); {
257		case bi&types.IsBoolean != 0:
258			bt.Encoding = llvm.DW_ATE_boolean
259		case bi&types.IsUnsigned != 0:
260			bt.Encoding = llvm.DW_ATE_unsigned
261		case bi&types.IsInteger != 0:
262			bt.Encoding = llvm.DW_ATE_signed
263		case bi&types.IsFloat != 0:
264			bt.Encoding = llvm.DW_ATE_float
265		case bi&types.IsComplex != 0:
266			bt.Encoding = llvm.DW_ATE_imaginary_float
267		case bi&types.IsUnsigned != 0:
268			bt.Encoding = llvm.DW_ATE_unsigned
269		default:
270			panic(fmt.Sprintf("unhandled: %#v", t))
271		}
272		return d.builder.CreateBasicType(bt)
273	}
274}
275
276func (d *DIBuilder) descriptorPointer(t *types.Pointer) llvm.Metadata {
277	return d.builder.CreatePointerType(llvm.DIPointerType{
278		Pointee:     d.DIType(t.Elem()),
279		SizeInBits:  uint64(d.sizes.Sizeof(t) * 8),
280		AlignInBits: uint32(d.sizes.Alignof(t) * 8),
281	})
282}
283
284func (d *DIBuilder) descriptorStruct(t *types.Struct, name string) llvm.Metadata {
285	fields := make([]*types.Var, t.NumFields())
286	for i := range fields {
287		fields[i] = t.Field(i)
288	}
289	offsets := d.sizes.Offsetsof(fields)
290	members := make([]llvm.Metadata, len(fields))
291	for i, f := range fields {
292		// TODO(axw) file/line where member is defined.
293		t := f.Type()
294		members[i] = d.builder.CreateMemberType(d.cu, llvm.DIMemberType{
295			Name:         f.Name(),
296			Type:         d.DIType(t),
297			SizeInBits:   uint64(d.sizes.Sizeof(t) * 8),
298			AlignInBits:  uint32(d.sizes.Alignof(t) * 8),
299			OffsetInBits: uint64(offsets[i] * 8),
300		})
301	}
302	// TODO(axw) file/line where struct is defined.
303	return d.builder.CreateStructType(d.cu, llvm.DIStructType{
304		Name:        name,
305		SizeInBits:  uint64(d.sizes.Sizeof(t) * 8),
306		AlignInBits: uint32(d.sizes.Alignof(t) * 8),
307		Elements:    members,
308	})
309}
310
311func (d *DIBuilder) descriptorNamed(t *types.Named) llvm.Metadata {
312	var diFile llvm.Metadata
313	var line int
314	if file := d.fset.File(t.Obj().Pos()); file != nil {
315		line = file.Line(t.Obj().Pos())
316		diFile = d.getFile(file)
317	}
318
319	// Create a placeholder for the named type, to terminate cycles.
320	name := t.Obj().Name()
321	placeholder := d.builder.CreateReplaceableCompositeType(d.scope(), llvm.DIReplaceableCompositeType{
322		Tag:  dwarf.TagStructType,
323		Name: name,
324		File: diFile,
325		Line: line,
326	})
327	d.types.Set(t, placeholder)
328
329	typedef := d.builder.CreateTypedef(llvm.DITypedef{
330		Type: d.DIType(t.Underlying()),
331		Name: name,
332		File: diFile,
333		Line: line,
334	})
335	placeholder.ReplaceAllUsesWith(typedef)
336	return typedef
337}
338
339func (d *DIBuilder) descriptorArray(t *types.Array, name string) llvm.Metadata {
340	return d.builder.CreateArrayType(llvm.DIArrayType{
341		SizeInBits:  uint64(d.sizes.Sizeof(t) * 8),
342		AlignInBits: uint32(d.sizes.Alignof(t) * 8),
343		ElementType: d.DIType(t.Elem()),
344		Subscripts:  []llvm.DISubrange{{Count: t.Len()}},
345	})
346}
347
348func (d *DIBuilder) descriptorSlice(t *types.Slice, name string) llvm.Metadata {
349	sliceStruct := types.NewStruct([]*types.Var{
350		types.NewVar(0, nil, "ptr", types.NewPointer(t.Elem())),
351		types.NewVar(0, nil, "len", types.Typ[types.Int]),
352		types.NewVar(0, nil, "cap", types.Typ[types.Int]),
353	}, nil)
354	return d.typeDebugDescriptor(sliceStruct, name)
355}
356
357func (d *DIBuilder) descriptorMap(t *types.Map, name string) llvm.Metadata {
358	// FIXME: This should be DW_TAG_pointer_type to __go_map.
359	return d.descriptorBasic(types.Typ[types.Uintptr], name)
360}
361
362func (d *DIBuilder) descriptorChan(t *types.Chan, name string) llvm.Metadata {
363	// FIXME: This should be DW_TAG_pointer_type to __go_channel.
364	return d.descriptorBasic(types.Typ[types.Uintptr], name)
365}
366
367func (d *DIBuilder) descriptorInterface(t *types.Interface, name string) llvm.Metadata {
368	ifaceStruct := types.NewStruct([]*types.Var{
369		types.NewVar(0, nil, "type", types.NewPointer(types.Typ[types.Uint8])),
370		types.NewVar(0, nil, "data", types.NewPointer(types.Typ[types.Uint8])),
371	}, nil)
372	return d.typeDebugDescriptor(ifaceStruct, name)
373}
374
375func (d *DIBuilder) descriptorSignature(t *types.Signature, name string) llvm.Metadata {
376	// If there's a receiver change the receiver to an
377	// additional (first) parameter, and take the value of
378	// the resulting signature instead.
379	if recv := t.Recv(); recv != nil {
380		params := t.Params()
381		paramvars := make([]*types.Var, int(params.Len()+1))
382		paramvars[0] = recv
383		for i := 0; i < int(params.Len()); i++ {
384			paramvars[i+1] = params.At(i)
385		}
386		params = types.NewTuple(paramvars...)
387		t := types.NewSignature(nil, nil, params, t.Results(), t.Variadic())
388		return d.typeDebugDescriptor(t, name)
389	}
390	if dt, ok := d.types.At(t).(llvm.Metadata); ok {
391		return dt
392	}
393
394	var returnType llvm.Metadata
395	results := t.Results()
396	switch n := results.Len(); n {
397	case 0:
398		returnType = d.DIType(nil) // void
399	case 1:
400		returnType = d.DIType(results.At(0).Type())
401	default:
402		fields := make([]*types.Var, results.Len())
403		for i := range fields {
404			f := results.At(i)
405			// Structs may not have multiple fields
406			// with the same name, excepting "_".
407			if f.Name() == "" {
408				f = types.NewVar(f.Pos(), f.Pkg(), "_", f.Type())
409			}
410			fields[i] = f
411		}
412		returnType = d.typeDebugDescriptor(types.NewStruct(fields, nil), "")
413	}
414
415	var paramTypes []llvm.Metadata
416	params := t.Params()
417	if params != nil && params.Len() > 0 {
418		paramTypes = make([]llvm.Metadata, params.Len()+1)
419		paramTypes[0] = returnType
420		for i := range paramTypes[1:] {
421			paramTypes[i+1] = d.DIType(params.At(i).Type())
422		}
423	} else {
424		paramTypes = []llvm.Metadata{returnType}
425	}
426
427	// TODO(axw) get position of type definition for File field
428	return d.builder.CreateSubroutineType(llvm.DISubroutineType{
429		Parameters: paramTypes,
430	})
431}
432