mirror of
https://github.com/tinygo-org/tinygo.git
synced 2026-08-04 11:07:46 +00:00
Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 71fa131ef9 |
@@ -82,7 +82,6 @@ commands:
|
|||||||
- run: tinygo build -size short -o test.elf -target=circuitplay-express examples/blinky1
|
- run: tinygo build -size short -o test.elf -target=circuitplay-express examples/blinky1
|
||||||
- run: tinygo build -size short -o test.elf -target=stm32f4disco examples/blinky1
|
- run: tinygo build -size short -o test.elf -target=stm32f4disco examples/blinky1
|
||||||
- run: tinygo build -size short -o test.elf -target=stm32f4disco examples/blinky2
|
- run: tinygo build -size short -o test.elf -target=stm32f4disco examples/blinky2
|
||||||
- run: tinygo build -size short -o test.elf -target=circuitplay-express examples/i2s
|
|
||||||
- run: tinygo build -o wasm.wasm -target=wasm examples/wasm/export
|
- run: tinygo build -o wasm.wasm -target=wasm examples/wasm/export
|
||||||
- run: tinygo build -o wasm.wasm -target=wasm examples/wasm/main
|
- run: tinygo build -o wasm.wasm -target=wasm examples/wasm/main
|
||||||
test-linux:
|
test-linux:
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ CGO_LDFLAGS=-L$(LLVM_BUILDDIR)/lib $(CLANG_LIBS) $(LLD_LIBS) $(shell $(LLVM_BUIL
|
|||||||
clean:
|
clean:
|
||||||
@rm -rf build
|
@rm -rf build
|
||||||
|
|
||||||
FMT_PATHS = ./*.go cgo compiler interp ir loader src/device/arm src/examples src/machine src/os src/reflect src/runtime src/sync src/syscall
|
FMT_PATHS = ./*.go compiler interp ir loader src/device/arm src/examples src/machine src/os src/reflect src/runtime src/sync src/syscall
|
||||||
fmt:
|
fmt:
|
||||||
@gofmt -l -w $(FMT_PATHS)
|
@gofmt -l -w $(FMT_PATHS)
|
||||||
fmt-check:
|
fmt-check:
|
||||||
|
|||||||
@@ -55,25 +55,6 @@ func (c *Compiler) expandFormalParamType(t llvm.Type) []llvm.Type {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Expand an argument type to a list of offsets from the start of the object.
|
|
||||||
// Used together with expandFormalParam to get the offset of each value from the
|
|
||||||
// start of the non-expanded value.
|
|
||||||
func (c *Compiler) expandFormalParamOffsets(t llvm.Type) []uint64 {
|
|
||||||
switch t.TypeKind() {
|
|
||||||
case llvm.StructTypeKind:
|
|
||||||
fields := c.flattenAggregateTypeOffsets(t)
|
|
||||||
if len(fields) <= MaxFieldsPerParam {
|
|
||||||
return fields
|
|
||||||
} else {
|
|
||||||
// failed to lower
|
|
||||||
return []uint64{0}
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
// TODO: split small arrays
|
|
||||||
return []uint64{0}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Equivalent of expandFormalParamType for parameter values.
|
// Equivalent of expandFormalParamType for parameter values.
|
||||||
func (c *Compiler) expandFormalParam(v llvm.Value) []llvm.Value {
|
func (c *Compiler) expandFormalParam(v llvm.Value) []llvm.Value {
|
||||||
switch v.Type().TypeKind() {
|
switch v.Type().TypeKind() {
|
||||||
@@ -111,27 +92,6 @@ func (c *Compiler) flattenAggregateType(t llvm.Type) []llvm.Type {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return the offsets from the start of the object if this object type were
|
|
||||||
// flattened like in flattenAggregate. Used together with flattenAggregate to
|
|
||||||
// know the start indices of each value in the non-flattened object.
|
|
||||||
func (c *Compiler) flattenAggregateTypeOffsets(t llvm.Type) []uint64 {
|
|
||||||
switch t.TypeKind() {
|
|
||||||
case llvm.StructTypeKind:
|
|
||||||
fields := make([]uint64, 0, t.StructElementTypesCount())
|
|
||||||
for fieldIndex, field := range t.StructElementTypes() {
|
|
||||||
suboffsets := c.flattenAggregateTypeOffsets(field)
|
|
||||||
offset := c.targetData.ElementOffset(t, fieldIndex)
|
|
||||||
for i := range suboffsets {
|
|
||||||
suboffsets[i] += offset
|
|
||||||
}
|
|
||||||
fields = append(fields, suboffsets...)
|
|
||||||
}
|
|
||||||
return fields
|
|
||||||
default:
|
|
||||||
return []uint64{0}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Break down a struct into its elementary types for argument passing. The value
|
// Break down a struct into its elementary types for argument passing. The value
|
||||||
// equivalent of flattenAggregateType
|
// equivalent of flattenAggregateType
|
||||||
func (c *Compiler) flattenAggregate(v llvm.Value) []llvm.Value {
|
func (c *Compiler) flattenAggregate(v llvm.Value) []llvm.Value {
|
||||||
|
|||||||
+45
-221
@@ -52,6 +52,7 @@ type Compiler struct {
|
|||||||
dibuilder *llvm.DIBuilder
|
dibuilder *llvm.DIBuilder
|
||||||
cu llvm.Metadata
|
cu llvm.Metadata
|
||||||
difiles map[string]llvm.Metadata
|
difiles map[string]llvm.Metadata
|
||||||
|
ditypes map[string]llvm.Metadata
|
||||||
machine llvm.TargetMachine
|
machine llvm.TargetMachine
|
||||||
targetData llvm.TargetData
|
targetData llvm.TargetData
|
||||||
intType llvm.Type
|
intType llvm.Type
|
||||||
@@ -95,6 +96,7 @@ func NewCompiler(pkgName string, config Config) (*Compiler, error) {
|
|||||||
c := &Compiler{
|
c := &Compiler{
|
||||||
Config: config,
|
Config: config,
|
||||||
difiles: make(map[string]llvm.Metadata),
|
difiles: make(map[string]llvm.Metadata),
|
||||||
|
ditypes: make(map[string]llvm.Metadata),
|
||||||
}
|
}
|
||||||
|
|
||||||
target, err := llvm.GetTargetFromTriple(config.Triple)
|
target, err := llvm.GetTargetFromTriple(config.Triple)
|
||||||
@@ -151,15 +153,11 @@ func (c *Compiler) TargetData() llvm.TargetData {
|
|||||||
func (c *Compiler) selectGC() string {
|
func (c *Compiler) selectGC() string {
|
||||||
gc := c.GC
|
gc := c.GC
|
||||||
if gc == "" {
|
if gc == "" {
|
||||||
gc = "leaking"
|
gc = "dumb"
|
||||||
}
|
}
|
||||||
return gc
|
return gc
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Compiler) gcIsPrecise() bool {
|
|
||||||
return c.GC == "precise"
|
|
||||||
}
|
|
||||||
|
|
||||||
// Compile the given package path or .go file path. Return an error when this
|
// Compile the given package path or .go file path. Return an error when this
|
||||||
// fails (in any stage).
|
// fails (in any stage).
|
||||||
func (c *Compiler) Compile(mainPath string) []error {
|
func (c *Compiler) Compile(mainPath string) []error {
|
||||||
@@ -199,7 +197,7 @@ func (c *Compiler) Compile(mainPath string) []error {
|
|||||||
},
|
},
|
||||||
ShouldOverlay: func(path string) bool {
|
ShouldOverlay: func(path string) bool {
|
||||||
switch path {
|
switch path {
|
||||||
case "machine", "os", "reflect", "runtime", "runtime/volatile", "sync":
|
case "machine", "os", "reflect", "runtime", "sync":
|
||||||
return true
|
return true
|
||||||
default:
|
default:
|
||||||
if strings.HasPrefix(path, "device/") || strings.HasPrefix(path, "examples/") {
|
if strings.HasPrefix(path, "device/") || strings.HasPrefix(path, "examples/") {
|
||||||
@@ -254,7 +252,7 @@ func (c *Compiler) Compile(mainPath string) []error {
|
|||||||
// Initialize debug information.
|
// Initialize debug information.
|
||||||
if c.Debug {
|
if c.Debug {
|
||||||
c.cu = c.dibuilder.CreateCompileUnit(llvm.DICompileUnit{
|
c.cu = c.dibuilder.CreateCompileUnit(llvm.DICompileUnit{
|
||||||
Language: 0xb, // DW_LANG_C99 (0xc, off-by-one?)
|
Language: llvm.DW_LANG_Go,
|
||||||
File: mainPath,
|
File: mainPath,
|
||||||
Dir: "",
|
Dir: "",
|
||||||
Producer: "TinyGo",
|
Producer: "TinyGo",
|
||||||
@@ -389,13 +387,6 @@ func (c *Compiler) Compile(mainPath string) []error {
|
|||||||
llvm.ConstInt(c.ctx.Int32Type(), 3, false).ConstantAsMetadata(), // DWARF version
|
llvm.ConstInt(c.ctx.Int32Type(), 3, false).ConstantAsMetadata(), // DWARF version
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
c.mod.AddNamedMetadataOperand("llvm.module.flags",
|
|
||||||
c.ctx.MDNode([]llvm.Metadata{
|
|
||||||
llvm.ConstInt(c.ctx.Int32Type(), 1, false).ConstantAsMetadata(),
|
|
||||||
llvm.GlobalContext().MDString("Dwarf Version"),
|
|
||||||
llvm.ConstInt(c.ctx.Int32Type(), 4, false).ConstantAsMetadata(),
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
c.dibuilder.Finalize()
|
c.dibuilder.Finalize()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -559,166 +550,39 @@ func isPointer(typ types.Type) bool {
|
|||||||
|
|
||||||
// Get the DWARF type for this Go type.
|
// Get the DWARF type for this Go type.
|
||||||
func (c *Compiler) getDIType(typ types.Type) llvm.Metadata {
|
func (c *Compiler) getDIType(typ types.Type) llvm.Metadata {
|
||||||
llvmType := c.getLLVMType(typ)
|
name := typ.String()
|
||||||
sizeInBytes := c.targetData.TypeAllocSize(llvmType)
|
if dityp, ok := c.ditypes[name]; ok {
|
||||||
switch typ := typ.(type) {
|
return dityp
|
||||||
case *types.Array:
|
} else {
|
||||||
return c.dibuilder.CreateArrayType(llvm.DIArrayType{
|
llvmType := c.getLLVMType(typ)
|
||||||
SizeInBits: sizeInBytes * 8,
|
sizeInBytes := c.targetData.TypeAllocSize(llvmType)
|
||||||
AlignInBits: uint32(c.targetData.ABITypeAlignment(llvmType)) * 8,
|
|
||||||
ElementType: c.getDIType(typ.Elem()),
|
|
||||||
Subscripts: []llvm.DISubrange{
|
|
||||||
llvm.DISubrange{
|
|
||||||
Lo: 0,
|
|
||||||
Count: typ.Len(),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})
|
|
||||||
case *types.Basic:
|
|
||||||
var encoding llvm.DwarfTypeEncoding
|
var encoding llvm.DwarfTypeEncoding
|
||||||
if typ.Info()&types.IsBoolean != 0 {
|
switch typ := typ.(type) {
|
||||||
encoding = llvm.DW_ATE_boolean
|
case *types.Basic:
|
||||||
} else if typ.Info()&types.IsFloat != 0 {
|
if typ.Info()&types.IsBoolean != 0 {
|
||||||
encoding = llvm.DW_ATE_float
|
encoding = llvm.DW_ATE_boolean
|
||||||
} else if typ.Info()&types.IsComplex != 0 {
|
} else if typ.Info()&types.IsFloat != 0 {
|
||||||
encoding = llvm.DW_ATE_complex_float
|
encoding = llvm.DW_ATE_float
|
||||||
} else if typ.Info()&types.IsUnsigned != 0 {
|
} else if typ.Info()&types.IsComplex != 0 {
|
||||||
encoding = llvm.DW_ATE_unsigned
|
encoding = llvm.DW_ATE_complex_float
|
||||||
} else if typ.Info()&types.IsInteger != 0 {
|
} else if typ.Info()&types.IsUnsigned != 0 {
|
||||||
encoding = llvm.DW_ATE_signed
|
encoding = llvm.DW_ATE_unsigned
|
||||||
} else if typ.Kind() == types.UnsafePointer {
|
} else if typ.Info()&types.IsInteger != 0 {
|
||||||
return c.dibuilder.CreatePointerType(llvm.DIPointerType{
|
encoding = llvm.DW_ATE_signed
|
||||||
Name: "unsafe.Pointer",
|
} else if typ.Kind() == types.UnsafePointer {
|
||||||
SizeInBits: c.targetData.TypeAllocSize(llvmType) * 8,
|
encoding = llvm.DW_ATE_address
|
||||||
AlignInBits: uint32(c.targetData.ABITypeAlignment(llvmType)) * 8,
|
}
|
||||||
AddressSpace: 0,
|
case *types.Pointer:
|
||||||
})
|
encoding = llvm.DW_ATE_address
|
||||||
} else if typ.Info()&types.IsString != 0 {
|
|
||||||
return c.dibuilder.CreateStructType(llvm.Metadata{}, llvm.DIStructType{
|
|
||||||
Name: "string",
|
|
||||||
SizeInBits: sizeInBytes * 8,
|
|
||||||
AlignInBits: uint32(c.targetData.ABITypeAlignment(llvmType)) * 8,
|
|
||||||
Elements: []llvm.Metadata{
|
|
||||||
c.dibuilder.CreateMemberType(llvm.Metadata{}, llvm.DIMemberType{
|
|
||||||
Name: "ptr",
|
|
||||||
SizeInBits: c.targetData.TypeAllocSize(c.i8ptrType) * 8,
|
|
||||||
AlignInBits: uint32(c.targetData.ABITypeAlignment(c.i8ptrType)) * 8,
|
|
||||||
OffsetInBits: 0,
|
|
||||||
Type: c.getDIType(types.NewPointer(types.Typ[types.Byte])),
|
|
||||||
}),
|
|
||||||
c.dibuilder.CreateMemberType(llvm.Metadata{}, llvm.DIMemberType{
|
|
||||||
Name: "len",
|
|
||||||
SizeInBits: c.targetData.TypeAllocSize(c.uintptrType) * 8,
|
|
||||||
AlignInBits: uint32(c.targetData.ABITypeAlignment(c.uintptrType)) * 8,
|
|
||||||
OffsetInBits: c.targetData.ElementOffset(llvmType, 1) * 8,
|
|
||||||
Type: c.getDIType(types.Typ[types.Uintptr]),
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
panic("unknown basic type")
|
|
||||||
}
|
}
|
||||||
return c.dibuilder.CreateBasicType(llvm.DIBasicType{
|
// TODO: other types
|
||||||
Name: typ.String(),
|
dityp = c.dibuilder.CreateBasicType(llvm.DIBasicType{
|
||||||
|
Name: name,
|
||||||
SizeInBits: sizeInBytes * 8,
|
SizeInBits: sizeInBytes * 8,
|
||||||
Encoding: encoding,
|
Encoding: encoding,
|
||||||
})
|
})
|
||||||
case *types.Chan:
|
c.ditypes[name] = dityp
|
||||||
return c.getDIType(types.NewPointer(c.ir.Program.ImportedPackage("runtime").Members["channel"].(*ssa.Type).Type()))
|
return dityp
|
||||||
case *types.Interface:
|
|
||||||
return c.getDIType(c.ir.Program.ImportedPackage("runtime").Members["_interface"].(*ssa.Type).Type())
|
|
||||||
case *types.Map:
|
|
||||||
return c.getDIType(types.NewPointer(c.ir.Program.ImportedPackage("runtime").Members["hashmap"].(*ssa.Type).Type()))
|
|
||||||
case *types.Named:
|
|
||||||
return c.dibuilder.CreateTypedef(llvm.DITypedef{
|
|
||||||
Type: c.getDIType(typ.Underlying()),
|
|
||||||
Name: typ.String(),
|
|
||||||
})
|
|
||||||
case *types.Pointer:
|
|
||||||
return c.dibuilder.CreatePointerType(llvm.DIPointerType{
|
|
||||||
Pointee: c.getDIType(typ.Elem()),
|
|
||||||
SizeInBits: c.targetData.TypeAllocSize(llvmType) * 8,
|
|
||||||
AlignInBits: uint32(c.targetData.ABITypeAlignment(llvmType)) * 8,
|
|
||||||
AddressSpace: 0,
|
|
||||||
})
|
|
||||||
case *types.Signature:
|
|
||||||
// actually a closure
|
|
||||||
fields := llvmType.StructElementTypes()
|
|
||||||
return c.dibuilder.CreateStructType(llvm.Metadata{}, llvm.DIStructType{
|
|
||||||
SizeInBits: sizeInBytes * 8,
|
|
||||||
AlignInBits: uint32(c.targetData.ABITypeAlignment(llvmType)) * 8,
|
|
||||||
Elements: []llvm.Metadata{
|
|
||||||
c.dibuilder.CreateMemberType(llvm.Metadata{}, llvm.DIMemberType{
|
|
||||||
Name: "context",
|
|
||||||
SizeInBits: c.targetData.TypeAllocSize(fields[1]) * 8,
|
|
||||||
AlignInBits: uint32(c.targetData.ABITypeAlignment(fields[1])) * 8,
|
|
||||||
OffsetInBits: 0,
|
|
||||||
Type: c.getDIType(types.Typ[types.UnsafePointer]),
|
|
||||||
}),
|
|
||||||
c.dibuilder.CreateMemberType(llvm.Metadata{}, llvm.DIMemberType{
|
|
||||||
Name: "fn",
|
|
||||||
SizeInBits: c.targetData.TypeAllocSize(fields[0]) * 8,
|
|
||||||
AlignInBits: uint32(c.targetData.ABITypeAlignment(fields[0])) * 8,
|
|
||||||
OffsetInBits: c.targetData.ElementOffset(llvmType, 1) * 8,
|
|
||||||
Type: c.getDIType(types.Typ[types.UnsafePointer]),
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
})
|
|
||||||
case *types.Slice:
|
|
||||||
fields := llvmType.StructElementTypes()
|
|
||||||
return c.dibuilder.CreateStructType(llvm.Metadata{}, llvm.DIStructType{
|
|
||||||
Name: typ.String(),
|
|
||||||
SizeInBits: sizeInBytes * 8,
|
|
||||||
AlignInBits: uint32(c.targetData.ABITypeAlignment(llvmType)) * 8,
|
|
||||||
Elements: []llvm.Metadata{
|
|
||||||
c.dibuilder.CreateMemberType(llvm.Metadata{}, llvm.DIMemberType{
|
|
||||||
Name: "ptr",
|
|
||||||
SizeInBits: c.targetData.TypeAllocSize(fields[0]) * 8,
|
|
||||||
AlignInBits: uint32(c.targetData.ABITypeAlignment(fields[0])) * 8,
|
|
||||||
OffsetInBits: 0,
|
|
||||||
Type: c.getDIType(types.NewPointer(typ.Elem())),
|
|
||||||
}),
|
|
||||||
c.dibuilder.CreateMemberType(llvm.Metadata{}, llvm.DIMemberType{
|
|
||||||
Name: "len",
|
|
||||||
SizeInBits: c.targetData.TypeAllocSize(c.uintptrType) * 8,
|
|
||||||
AlignInBits: uint32(c.targetData.ABITypeAlignment(c.uintptrType)) * 8,
|
|
||||||
OffsetInBits: c.targetData.ElementOffset(llvmType, 1) * 8,
|
|
||||||
Type: c.getDIType(types.Typ[types.Uintptr]),
|
|
||||||
}),
|
|
||||||
c.dibuilder.CreateMemberType(llvm.Metadata{}, llvm.DIMemberType{
|
|
||||||
Name: "cap",
|
|
||||||
SizeInBits: c.targetData.TypeAllocSize(c.uintptrType) * 8,
|
|
||||||
AlignInBits: uint32(c.targetData.ABITypeAlignment(c.uintptrType)) * 8,
|
|
||||||
OffsetInBits: c.targetData.ElementOffset(llvmType, 2) * 8,
|
|
||||||
Type: c.getDIType(types.Typ[types.Uintptr]),
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
})
|
|
||||||
case *types.Struct:
|
|
||||||
elements := make([]llvm.Metadata, typ.NumFields())
|
|
||||||
for i := range elements {
|
|
||||||
field := typ.Field(i)
|
|
||||||
fieldType := field.Type()
|
|
||||||
if _, ok := fieldType.Underlying().(*types.Pointer); ok {
|
|
||||||
// XXX hack to avoid recursive types
|
|
||||||
fieldType = types.Typ[types.UnsafePointer]
|
|
||||||
}
|
|
||||||
llvmField := c.getLLVMType(fieldType)
|
|
||||||
elements[i] = c.dibuilder.CreateMemberType(llvm.Metadata{}, llvm.DIMemberType{
|
|
||||||
Name: field.Name(),
|
|
||||||
SizeInBits: c.targetData.TypeAllocSize(llvmField) * 8,
|
|
||||||
AlignInBits: uint32(c.targetData.ABITypeAlignment(llvmField)) * 8,
|
|
||||||
OffsetInBits: c.targetData.ElementOffset(llvmType, i) * 8,
|
|
||||||
Type: c.getDIType(fieldType),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
return c.dibuilder.CreateStructType(llvm.Metadata{}, llvm.DIStructType{
|
|
||||||
SizeInBits: sizeInBytes * 8,
|
|
||||||
AlignInBits: uint32(c.targetData.ABITypeAlignment(llvmType)) * 8,
|
|
||||||
Elements: elements,
|
|
||||||
})
|
|
||||||
default:
|
|
||||||
panic("unknown type while generating DWARF debug type: " + typ.String())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -832,14 +696,6 @@ func (c *Compiler) parseFunc(frame *Frame) {
|
|||||||
frame.fn.LLVMFn.SetFunctionCallConv(85) // CallingConv::AVR_SIGNAL
|
frame.fn.LLVMFn.SetFunctionCallConv(85) // CallingConv::AVR_SIGNAL
|
||||||
}
|
}
|
||||||
|
|
||||||
// Some functions have a pragma controlling the inlining level.
|
|
||||||
switch frame.fn.Inline() {
|
|
||||||
case ir.InlineHint:
|
|
||||||
// Add LLVM inline hint to functions with //go:inline pragma.
|
|
||||||
inline := c.ctx.CreateEnumAttribute(llvm.AttributeKindID("inlinehint"), 0)
|
|
||||||
frame.fn.LLVMFn.AddFunctionAttr(inline)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add debug info, if needed.
|
// Add debug info, if needed.
|
||||||
if c.Debug {
|
if c.Debug {
|
||||||
if frame.fn.Synthetic == "package initializer" {
|
if frame.fn.Synthetic == "package initializer" {
|
||||||
@@ -877,30 +733,15 @@ func (c *Compiler) parseFunc(frame *Frame) {
|
|||||||
// Add debug information to this parameter (if available)
|
// Add debug information to this parameter (if available)
|
||||||
if c.Debug && frame.fn.Syntax() != nil {
|
if c.Debug && frame.fn.Syntax() != nil {
|
||||||
pos := c.ir.Program.Fset.Position(frame.fn.Syntax().Pos())
|
pos := c.ir.Program.Fset.Position(frame.fn.Syntax().Pos())
|
||||||
diType := c.getDIType(param.Type())
|
c.dibuilder.CreateParameterVariable(frame.difunc, llvm.DIParameterVariable{
|
||||||
dbgParam := c.dibuilder.CreateParameterVariable(frame.difunc, llvm.DIParameterVariable{
|
|
||||||
Name: param.Name(),
|
Name: param.Name(),
|
||||||
File: c.difiles[pos.Filename],
|
File: c.difiles[pos.Filename],
|
||||||
Line: pos.Line,
|
Line: pos.Line,
|
||||||
Type: diType,
|
Type: c.getDIType(param.Type()),
|
||||||
AlwaysPreserve: true,
|
AlwaysPreserve: true,
|
||||||
ArgNo: i + 1,
|
ArgNo: i + 1,
|
||||||
})
|
})
|
||||||
loc := c.builder.GetCurrentDebugLocation()
|
// TODO: set the value of this parameter.
|
||||||
if len(fields) == 1 {
|
|
||||||
expr := c.dibuilder.CreateExpression(nil)
|
|
||||||
c.dibuilder.InsertValueAtEnd(fields[0], dbgParam, expr, loc, entryBlock)
|
|
||||||
} else {
|
|
||||||
fieldOffsets := c.expandFormalParamOffsets(llvmType)
|
|
||||||
for i, field := range fields {
|
|
||||||
expr := c.dibuilder.CreateExpression([]int64{
|
|
||||||
0x1000, // DW_OP_LLVM_fragment
|
|
||||||
int64(fieldOffsets[i]) * 8, // offset in bits
|
|
||||||
int64(c.targetData.TypeAllocSize(field.Type())) * 8, // size in bits
|
|
||||||
})
|
|
||||||
c.dibuilder.InsertValueAtEnd(field, dbgParam, expr, loc, entryBlock)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1066,7 +907,6 @@ func (c *Compiler) parseInstr(frame *Frame, instr ssa.Instruction) {
|
|||||||
case *ssa.Store:
|
case *ssa.Store:
|
||||||
llvmAddr := c.getValue(frame, instr.Addr)
|
llvmAddr := c.getValue(frame, instr.Addr)
|
||||||
llvmVal := c.getValue(frame, instr.Val)
|
llvmVal := c.getValue(frame, instr.Val)
|
||||||
c.emitNilCheck(frame, llvmAddr, "store")
|
|
||||||
if c.targetData.TypeAllocSize(llvmVal.Type()) == 0 {
|
if c.targetData.TypeAllocSize(llvmVal.Type()) == 0 {
|
||||||
// nothing to store
|
// nothing to store
|
||||||
return
|
return
|
||||||
@@ -1277,22 +1117,17 @@ func (c *Compiler) parseCall(frame *Frame, instr *ssa.CallCommon) (llvm.Value, e
|
|||||||
|
|
||||||
// Try to call the function directly for trivially static calls.
|
// Try to call the function directly for trivially static calls.
|
||||||
if fn := instr.StaticCallee(); fn != nil {
|
if fn := instr.StaticCallee(); fn != nil {
|
||||||
name := fn.RelString(nil)
|
switch fn.RelString(nil) {
|
||||||
switch {
|
case "device/arm.ReadRegister":
|
||||||
case name == "device/arm.ReadRegister":
|
|
||||||
return c.emitReadRegister(instr.Args)
|
return c.emitReadRegister(instr.Args)
|
||||||
case name == "device/arm.Asm" || name == "device/avr.Asm":
|
case "device/arm.Asm", "device/avr.Asm":
|
||||||
return c.emitAsm(instr.Args)
|
return c.emitAsm(instr.Args)
|
||||||
case name == "device/arm.AsmFull" || name == "device/avr.AsmFull":
|
case "device/arm.AsmFull", "device/avr.AsmFull":
|
||||||
return c.emitAsmFull(frame, instr)
|
return c.emitAsmFull(frame, instr)
|
||||||
case strings.HasPrefix(name, "device/arm.SVCall"):
|
case "device/arm.SVCall0", "device/arm.SVCall1", "device/arm.SVCall2", "device/arm.SVCall3", "device/arm.SVCall4":
|
||||||
return c.emitSVCall(frame, instr.Args)
|
return c.emitSVCall(frame, instr.Args)
|
||||||
case strings.HasPrefix(name, "syscall.Syscall"):
|
case "syscall.Syscall", "syscall.Syscall6", "syscall.Syscall9":
|
||||||
return c.emitSyscall(frame, instr)
|
return c.emitSyscall(frame, instr)
|
||||||
case strings.HasPrefix(name, "runtime/volatile.Load"):
|
|
||||||
return c.emitVolatileLoad(frame, instr)
|
|
||||||
case strings.HasPrefix(name, "runtime/volatile.Store"):
|
|
||||||
return c.emitVolatileStore(frame, instr)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
targetFunc := c.ir.GetFunction(fn)
|
targetFunc := c.ir.GetFunction(fn)
|
||||||
@@ -1581,7 +1416,7 @@ func (c *Compiler) parseExpr(frame *Frame, expr ssa.Value) (llvm.Value, error) {
|
|||||||
return c.parseMakeClosure(frame, expr)
|
return c.parseMakeClosure(frame, expr)
|
||||||
case *ssa.MakeInterface:
|
case *ssa.MakeInterface:
|
||||||
val := c.getValue(frame, expr.X)
|
val := c.getValue(frame, expr.X)
|
||||||
return c.parseMakeInterface(val, expr.X.Type(), expr.Pos()), nil
|
return c.parseMakeInterface(val, expr.X.Type(), expr.Pos())
|
||||||
case *ssa.MakeMap:
|
case *ssa.MakeMap:
|
||||||
mapType := expr.Type().Underlying().(*types.Map)
|
mapType := expr.Type().Underlying().(*types.Map)
|
||||||
llvmKeyType := c.getLLVMType(mapType.Key().Underlying())
|
llvmKeyType := c.getLLVMType(mapType.Key().Underlying())
|
||||||
@@ -1590,16 +1425,7 @@ func (c *Compiler) parseExpr(frame *Frame, expr ssa.Value) (llvm.Value, error) {
|
|||||||
valueSize := c.targetData.TypeAllocSize(llvmValueType)
|
valueSize := c.targetData.TypeAllocSize(llvmValueType)
|
||||||
llvmKeySize := llvm.ConstInt(c.ctx.Int8Type(), keySize, false)
|
llvmKeySize := llvm.ConstInt(c.ctx.Int8Type(), keySize, false)
|
||||||
llvmValueSize := llvm.ConstInt(c.ctx.Int8Type(), valueSize, false)
|
llvmValueSize := llvm.ConstInt(c.ctx.Int8Type(), valueSize, false)
|
||||||
sizeHint := llvm.ConstInt(c.uintptrType, 8, false)
|
hashmap := c.createRuntimeCall("hashmapMake", []llvm.Value{llvmKeySize, llvmValueSize}, "")
|
||||||
if expr.Reserve != nil {
|
|
||||||
sizeHint = c.getValue(frame, expr.Reserve)
|
|
||||||
var err error
|
|
||||||
sizeHint, err = c.parseConvert(expr.Reserve.Type(), types.Typ[types.Uintptr], sizeHint, expr.Pos())
|
|
||||||
if err != nil {
|
|
||||||
return llvm.Value{}, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
hashmap := c.createRuntimeCall("hashmapMake", []llvm.Value{llvmKeySize, llvmValueSize, sizeHint}, "")
|
|
||||||
return hashmap, nil
|
return hashmap, nil
|
||||||
case *ssa.MakeSlice:
|
case *ssa.MakeSlice:
|
||||||
sliceLen := c.getValue(frame, expr.Len)
|
sliceLen := c.getValue(frame, expr.Len)
|
||||||
@@ -2118,12 +1944,10 @@ func (c *Compiler) parseBinOp(op token.Token, typ types.Type, x, y llvm.Value, p
|
|||||||
default:
|
default:
|
||||||
return llvm.Value{}, c.makeError(pos, "binop on interface: "+op.String())
|
return llvm.Value{}, c.makeError(pos, "binop on interface: "+op.String())
|
||||||
}
|
}
|
||||||
case *types.Chan, *types.Map, *types.Pointer:
|
case *types.Map, *types.Pointer:
|
||||||
// Maps are in general not comparable, but can be compared against nil
|
// Maps are in general not comparable, but can be compared against nil
|
||||||
// (which is a nil pointer). This means they can be trivially compared
|
// (which is a nil pointer). This means they can be trivially compared
|
||||||
// by treating them as a pointer.
|
// by treating them as a pointer.
|
||||||
// Channels behave as pointers in that they are equal as long as they
|
|
||||||
// are created with the same call to make or if both are nil.
|
|
||||||
switch op {
|
switch op {
|
||||||
case token.EQL: // ==
|
case token.EQL: // ==
|
||||||
return c.builder.CreateICmp(llvm.IntEQ, x, y, ""), nil
|
return c.builder.CreateICmp(llvm.IntEQ, x, y, ""), nil
|
||||||
|
|||||||
@@ -1,104 +0,0 @@
|
|||||||
package compiler
|
|
||||||
|
|
||||||
import (
|
|
||||||
"math/big"
|
|
||||||
|
|
||||||
"tinygo.org/x/go-llvm"
|
|
||||||
)
|
|
||||||
|
|
||||||
func (c *Compiler) addGlobalsBitmap() {
|
|
||||||
if c.mod.NamedGlobal("runtime.trackedGlobalsStart").IsNil() {
|
|
||||||
return // nothing to do: no GC in use
|
|
||||||
}
|
|
||||||
|
|
||||||
var trackedGlobals []llvm.Value
|
|
||||||
var trackedGlobalTypes []llvm.Type
|
|
||||||
for global := c.mod.FirstGlobal(); !global.IsNil(); global = llvm.NextGlobal(global) {
|
|
||||||
if global.IsDeclaration() {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
typ := global.Type().ElementType()
|
|
||||||
ptrs := c.getPointerBitmap(typ, global.Name())
|
|
||||||
if ptrs.BitLen() == 0 {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
trackedGlobals = append(trackedGlobals, global)
|
|
||||||
trackedGlobalTypes = append(trackedGlobalTypes, typ)
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
globalsBundleType := c.ctx.StructType(trackedGlobalTypes, false)
|
|
||||||
globalsBundle := llvm.AddGlobal(c.mod, globalsBundleType, "tinygo.trackedGlobals")
|
|
||||||
globalsBundle.SetLinkage(llvm.InternalLinkage)
|
|
||||||
globalsBundle.SetUnnamedAddr(true)
|
|
||||||
initializer := llvm.Undef(globalsBundleType)
|
|
||||||
for i, global := range trackedGlobals {
|
|
||||||
initializer = llvm.ConstInsertValue(initializer, global.Initializer(), []uint32{uint32(i)})
|
|
||||||
gep := llvm.ConstGEP(globalsBundle, []llvm.Value{
|
|
||||||
llvm.ConstInt(c.ctx.Int32Type(), 0, false),
|
|
||||||
llvm.ConstInt(c.ctx.Int32Type(), uint64(i), false),
|
|
||||||
})
|
|
||||||
global.ReplaceAllUsesWith(gep)
|
|
||||||
global.EraseFromParentAsGlobal()
|
|
||||||
}
|
|
||||||
globalsBundle.SetInitializer(initializer)
|
|
||||||
|
|
||||||
trackedGlobalsStart := llvm.ConstPtrToInt(globalsBundle, c.uintptrType)
|
|
||||||
c.mod.NamedGlobal("runtime.trackedGlobalsStart").SetInitializer(trackedGlobalsStart)
|
|
||||||
|
|
||||||
alignment := c.targetData.PrefTypeAlignment(c.i8ptrType)
|
|
||||||
trackedGlobalsLength := llvm.ConstInt(c.uintptrType, c.targetData.TypeAllocSize(globalsBundleType)/uint64(alignment), false)
|
|
||||||
c.mod.NamedGlobal("runtime.trackedGlobalsLength").SetInitializer(trackedGlobalsLength)
|
|
||||||
|
|
||||||
bitmapBytes := c.getPointerBitmap(globalsBundleType, "globals bundle").Bytes()
|
|
||||||
bitmapValues := make([]llvm.Value, len(bitmapBytes))
|
|
||||||
for i, b := range bitmapBytes {
|
|
||||||
bitmapValues[len(bitmapBytes)-i-1] = llvm.ConstInt(c.ctx.Int8Type(), uint64(b), false)
|
|
||||||
}
|
|
||||||
bitmapArray := llvm.ConstArray(llvm.ArrayType(c.ctx.Int8Type(), len(bitmapBytes)), bitmapValues)
|
|
||||||
bitmapNew := llvm.AddGlobal(c.mod, bitmapArray.Type(), "runtime.trackedGlobalsBitmap.tmp")
|
|
||||||
bitmapOld := c.mod.NamedGlobal("runtime.trackedGlobalsBitmap")
|
|
||||||
bitmapOld.ReplaceAllUsesWith(bitmapNew)
|
|
||||||
bitmapNew.SetInitializer(bitmapArray)
|
|
||||||
bitmapNew.SetName("runtime.trackedGlobalsBitmap")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Compiler) getPointerBitmap(typ llvm.Type, name string) *big.Int {
|
|
||||||
alignment := c.targetData.PrefTypeAlignment(c.i8ptrType)
|
|
||||||
switch typ.TypeKind() {
|
|
||||||
case llvm.IntegerTypeKind, llvm.FloatTypeKind, llvm.DoubleTypeKind:
|
|
||||||
return big.NewInt(0)
|
|
||||||
case llvm.PointerTypeKind:
|
|
||||||
return big.NewInt(1)
|
|
||||||
case llvm.StructTypeKind:
|
|
||||||
ptrs := big.NewInt(0)
|
|
||||||
for i, subtyp := range typ.StructElementTypes() {
|
|
||||||
subptrs := c.getPointerBitmap(subtyp, name)
|
|
||||||
if subptrs.BitLen() == 0 {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
offset := c.targetData.ElementOffset(typ, i)
|
|
||||||
if offset%uint64(alignment) != 0 {
|
|
||||||
panic("precise GC: global contains unaligned pointer: " + name)
|
|
||||||
}
|
|
||||||
subptrs.Lsh(subptrs, uint(offset)/uint(alignment))
|
|
||||||
ptrs.Or(ptrs, subptrs)
|
|
||||||
}
|
|
||||||
return ptrs
|
|
||||||
case llvm.ArrayTypeKind:
|
|
||||||
subtyp := typ.ElementType()
|
|
||||||
subptrs := c.getPointerBitmap(subtyp, name)
|
|
||||||
ptrs := big.NewInt(0)
|
|
||||||
if subptrs.BitLen() == 0 {
|
|
||||||
return ptrs
|
|
||||||
}
|
|
||||||
elementSize := c.targetData.TypeAllocSize(subtyp)
|
|
||||||
for i := 0; i < typ.ArrayLength(); i++ {
|
|
||||||
ptrs.Lsh(ptrs, uint(elementSize)/uint(alignment))
|
|
||||||
ptrs.Or(ptrs, subptrs)
|
|
||||||
}
|
|
||||||
return ptrs
|
|
||||||
default:
|
|
||||||
panic("unknown type kind of global: " + name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
+18
-12
@@ -22,10 +22,13 @@ import (
|
|||||||
// value field.
|
// value field.
|
||||||
//
|
//
|
||||||
// An interface value is a {typecode, value} tuple, or {i16, i8*} to be exact.
|
// An interface value is a {typecode, value} tuple, or {i16, i8*} to be exact.
|
||||||
func (c *Compiler) parseMakeInterface(val llvm.Value, typ types.Type, pos token.Pos) llvm.Value {
|
func (c *Compiler) parseMakeInterface(val llvm.Value, typ types.Type, pos token.Pos) (llvm.Value, error) {
|
||||||
itfValue := c.emitPointerPack([]llvm.Value{val})
|
itfValue := c.emitPointerPack([]llvm.Value{val})
|
||||||
itfTypeCodeGlobal := c.getTypeCode(typ)
|
itfTypeCodeGlobal := c.getTypeCode(typ)
|
||||||
itfMethodSetGlobal := c.getTypeMethodSet(typ)
|
itfMethodSetGlobal, err := c.getTypeMethodSet(typ)
|
||||||
|
if err != nil {
|
||||||
|
return llvm.Value{}, nil
|
||||||
|
}
|
||||||
itfConcreteTypeGlobal := c.mod.NamedGlobal("typeInInterface:" + itfTypeCodeGlobal.Name())
|
itfConcreteTypeGlobal := c.mod.NamedGlobal("typeInInterface:" + itfTypeCodeGlobal.Name())
|
||||||
if itfConcreteTypeGlobal.IsNil() {
|
if itfConcreteTypeGlobal.IsNil() {
|
||||||
typeInInterface := c.mod.GetTypeByName("runtime.typeInInterface")
|
typeInInterface := c.mod.GetTypeByName("runtime.typeInInterface")
|
||||||
@@ -38,7 +41,7 @@ func (c *Compiler) parseMakeInterface(val llvm.Value, typ types.Type, pos token.
|
|||||||
itf := llvm.Undef(c.mod.GetTypeByName("runtime._interface"))
|
itf := llvm.Undef(c.mod.GetTypeByName("runtime._interface"))
|
||||||
itf = c.builder.CreateInsertValue(itf, itfTypeCode, 0, "")
|
itf = c.builder.CreateInsertValue(itf, itfTypeCode, 0, "")
|
||||||
itf = c.builder.CreateInsertValue(itf, itfValue, 1, "")
|
itf = c.builder.CreateInsertValue(itf, itfValue, 1, "")
|
||||||
return itf
|
return itf, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// getTypeCode returns a reference to a type code.
|
// getTypeCode returns a reference to a type code.
|
||||||
@@ -152,18 +155,18 @@ func getTypeCodeName(t types.Type) string {
|
|||||||
|
|
||||||
// getTypeMethodSet returns a reference (GEP) to a global method set. This
|
// getTypeMethodSet returns a reference (GEP) to a global method set. This
|
||||||
// method set should be unreferenced after the interface lowering pass.
|
// method set should be unreferenced after the interface lowering pass.
|
||||||
func (c *Compiler) getTypeMethodSet(typ types.Type) llvm.Value {
|
func (c *Compiler) getTypeMethodSet(typ types.Type) (llvm.Value, error) {
|
||||||
global := c.mod.NamedGlobal(typ.String() + "$methodset")
|
global := c.mod.NamedGlobal(typ.String() + "$methodset")
|
||||||
zero := llvm.ConstInt(c.ctx.Int32Type(), 0, false)
|
zero := llvm.ConstInt(c.ctx.Int32Type(), 0, false)
|
||||||
if !global.IsNil() {
|
if !global.IsNil() {
|
||||||
// the method set already exists
|
// the method set already exists
|
||||||
return llvm.ConstGEP(global, []llvm.Value{zero, zero})
|
return llvm.ConstGEP(global, []llvm.Value{zero, zero}), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
ms := c.ir.Program.MethodSets.MethodSet(typ)
|
ms := c.ir.Program.MethodSets.MethodSet(typ)
|
||||||
if ms.Len() == 0 {
|
if ms.Len() == 0 {
|
||||||
// no methods, so can leave that one out
|
// no methods, so can leave that one out
|
||||||
return llvm.ConstPointerNull(llvm.PointerType(c.mod.GetTypeByName("runtime.interfaceMethodInfo"), 0))
|
return llvm.ConstPointerNull(llvm.PointerType(c.mod.GetTypeByName("runtime.interfaceMethodInfo"), 0)), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
methods := make([]llvm.Value, ms.Len())
|
methods := make([]llvm.Value, ms.Len())
|
||||||
@@ -176,7 +179,10 @@ func (c *Compiler) getTypeMethodSet(typ types.Type) llvm.Value {
|
|||||||
// compiler error, so panic
|
// compiler error, so panic
|
||||||
panic("cannot find function: " + f.LinkName())
|
panic("cannot find function: " + f.LinkName())
|
||||||
}
|
}
|
||||||
fn := c.getInterfaceInvokeWrapper(f)
|
fn, err := c.getInterfaceInvokeWrapper(f)
|
||||||
|
if err != nil {
|
||||||
|
return llvm.Value{}, err
|
||||||
|
}
|
||||||
methodInfo := llvm.ConstNamedStruct(interfaceMethodInfoType, []llvm.Value{
|
methodInfo := llvm.ConstNamedStruct(interfaceMethodInfoType, []llvm.Value{
|
||||||
signatureGlobal,
|
signatureGlobal,
|
||||||
llvm.ConstPtrToInt(fn, c.uintptrType),
|
llvm.ConstPtrToInt(fn, c.uintptrType),
|
||||||
@@ -189,7 +195,7 @@ func (c *Compiler) getTypeMethodSet(typ types.Type) llvm.Value {
|
|||||||
global.SetInitializer(value)
|
global.SetInitializer(value)
|
||||||
global.SetGlobalConstant(true)
|
global.SetGlobalConstant(true)
|
||||||
global.SetLinkage(llvm.PrivateLinkage)
|
global.SetLinkage(llvm.PrivateLinkage)
|
||||||
return llvm.ConstGEP(global, []llvm.Value{zero, zero})
|
return llvm.ConstGEP(global, []llvm.Value{zero, zero}), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// getInterfaceMethodSet returns a global variable with the method set of the
|
// getInterfaceMethodSet returns a global variable with the method set of the
|
||||||
@@ -359,12 +365,12 @@ type interfaceInvokeWrapper struct {
|
|||||||
// the underlying value, dereferences it, and calls the real method. This
|
// the underlying value, dereferences it, and calls the real method. This
|
||||||
// wrapper is only needed when the interface value actually doesn't fit in a
|
// wrapper is only needed when the interface value actually doesn't fit in a
|
||||||
// pointer and a pointer to the value must be created.
|
// pointer and a pointer to the value must be created.
|
||||||
func (c *Compiler) getInterfaceInvokeWrapper(f *ir.Function) llvm.Value {
|
func (c *Compiler) getInterfaceInvokeWrapper(f *ir.Function) (llvm.Value, error) {
|
||||||
wrapperName := f.LinkName() + "$invoke"
|
wrapperName := f.LinkName() + "$invoke"
|
||||||
wrapper := c.mod.NamedFunction(wrapperName)
|
wrapper := c.mod.NamedFunction(wrapperName)
|
||||||
if !wrapper.IsNil() {
|
if !wrapper.IsNil() {
|
||||||
// Wrapper already created. Return it directly.
|
// Wrapper already created. Return it directly.
|
||||||
return wrapper
|
return wrapper, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the expanded receiver type.
|
// Get the expanded receiver type.
|
||||||
@@ -377,7 +383,7 @@ func (c *Compiler) getInterfaceInvokeWrapper(f *ir.Function) llvm.Value {
|
|||||||
// Casting a function signature to a different signature and calling it
|
// Casting a function signature to a different signature and calling it
|
||||||
// with a receiver pointer bitcasted to *i8 (as done in calls on an
|
// with a receiver pointer bitcasted to *i8 (as done in calls on an
|
||||||
// interface) is hopefully a safe (defined) operation.
|
// interface) is hopefully a safe (defined) operation.
|
||||||
return f.LLVMFn
|
return f.LLVMFn, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// create wrapper function
|
// create wrapper function
|
||||||
@@ -390,7 +396,7 @@ func (c *Compiler) getInterfaceInvokeWrapper(f *ir.Function) llvm.Value {
|
|||||||
wrapper: wrapper,
|
wrapper: wrapper,
|
||||||
receiverType: receiverType,
|
receiverType: receiverType,
|
||||||
})
|
})
|
||||||
return wrapper
|
return wrapper, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// createInterfaceInvokeWrapper finishes the work of getInterfaceInvokeWrapper,
|
// createInterfaceInvokeWrapper finishes the work of getInterfaceInvokeWrapper,
|
||||||
|
|||||||
@@ -22,36 +22,6 @@ func getUses(value llvm.Value) []llvm.Value {
|
|||||||
return uses
|
return uses
|
||||||
}
|
}
|
||||||
|
|
||||||
// createEntryBlockAlloca creates a new alloca in the entry block, even though
|
|
||||||
// the IR builder is located elsewhere. It assumes that the insert point is
|
|
||||||
// after the last instruction in the current block. Also, it adds lifetime
|
|
||||||
// information to the IR signalling that the alloca won't be used before this
|
|
||||||
// point.
|
|
||||||
//
|
|
||||||
// This is useful for creating temporary allocas for intrinsics. Don't forget to
|
|
||||||
// end the lifetime after you're done with it.
|
|
||||||
func (c *Compiler) createEntryBlockAlloca(t llvm.Type, name string) (alloca, bitcast, size llvm.Value) {
|
|
||||||
currentBlock := c.builder.GetInsertBlock()
|
|
||||||
c.builder.SetInsertPointBefore(currentBlock.Parent().EntryBasicBlock().FirstInstruction())
|
|
||||||
alloca = c.builder.CreateAlloca(t, name)
|
|
||||||
c.builder.SetInsertPointAtEnd(currentBlock)
|
|
||||||
bitcast = c.builder.CreateBitCast(alloca, c.i8ptrType, name+".bitcast")
|
|
||||||
size = llvm.ConstInt(c.ctx.Int64Type(), c.targetData.TypeAllocSize(t), false)
|
|
||||||
c.builder.CreateCall(c.getLifetimeStartFunc(), []llvm.Value{size, bitcast}, "")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// getLifetimeStartFunc returns the llvm.lifetime.start intrinsic and creates it
|
|
||||||
// first if it doesn't exist yet.
|
|
||||||
func (c *Compiler) getLifetimeStartFunc() llvm.Value {
|
|
||||||
fn := c.mod.NamedFunction("llvm.lifetime.start.p0i8")
|
|
||||||
if fn.IsNil() {
|
|
||||||
fnType := llvm.FunctionType(c.ctx.VoidType(), []llvm.Type{c.ctx.Int64Type(), c.i8ptrType}, false)
|
|
||||||
fn = llvm.AddFunction(c.mod, "llvm.lifetime.start.p0i8", fnType)
|
|
||||||
}
|
|
||||||
return fn
|
|
||||||
}
|
|
||||||
|
|
||||||
// getLifetimeEndFunc returns the llvm.lifetime.end intrinsic and creates it
|
// getLifetimeEndFunc returns the llvm.lifetime.end intrinsic and creates it
|
||||||
// first if it doesn't exist yet.
|
// first if it doesn't exist yet.
|
||||||
func (c *Compiler) getLifetimeEndFunc() llvm.Value {
|
func (c *Compiler) getLifetimeEndFunc() llvm.Value {
|
||||||
|
|||||||
+12
-26
@@ -11,13 +11,8 @@ import (
|
|||||||
|
|
||||||
func (c *Compiler) emitMapLookup(keyType, valueType types.Type, m, key llvm.Value, commaOk bool, pos token.Pos) (llvm.Value, error) {
|
func (c *Compiler) emitMapLookup(keyType, valueType types.Type, m, key llvm.Value, commaOk bool, pos token.Pos) (llvm.Value, error) {
|
||||||
llvmValueType := c.getLLVMType(valueType)
|
llvmValueType := c.getLLVMType(valueType)
|
||||||
|
mapValueAlloca := c.builder.CreateAlloca(llvmValueType, "hashmap.value")
|
||||||
// Allocate the memory for the resulting type. Do not zero this memory: it
|
mapValuePtr := c.builder.CreateBitCast(mapValueAlloca, c.i8ptrType, "hashmap.valueptr")
|
||||||
// will be zeroed by the hashmap get implementation if the key is not
|
|
||||||
// present in the map.
|
|
||||||
mapValueAlloca, mapValuePtr, mapValueSize := c.createEntryBlockAlloca(llvmValueType, "hashmap.value")
|
|
||||||
|
|
||||||
// Do the lookup. How it is done depends on the key type.
|
|
||||||
var commaOkValue llvm.Value
|
var commaOkValue llvm.Value
|
||||||
if t, ok := keyType.(*types.Basic); ok && t.Info()&types.IsString != 0 {
|
if t, ok := keyType.(*types.Basic); ok && t.Info()&types.IsString != 0 {
|
||||||
// key is a string
|
// key is a string
|
||||||
@@ -25,24 +20,15 @@ func (c *Compiler) emitMapLookup(keyType, valueType types.Type, m, key llvm.Valu
|
|||||||
commaOkValue = c.createRuntimeCall("hashmapStringGet", params, "")
|
commaOkValue = c.createRuntimeCall("hashmapStringGet", params, "")
|
||||||
} else if hashmapIsBinaryKey(keyType) {
|
} else if hashmapIsBinaryKey(keyType) {
|
||||||
// key can be compared with runtime.memequal
|
// key can be compared with runtime.memequal
|
||||||
// Store the key in an alloca, in the entry block to avoid dynamic stack
|
keyAlloca := c.builder.CreateAlloca(key.Type(), "hashmap.key")
|
||||||
// growth.
|
c.builder.CreateStore(key, keyAlloca)
|
||||||
mapKeyAlloca, mapKeyPtr, mapKeySize := c.createEntryBlockAlloca(key.Type(), "hashmap.key")
|
keyPtr := c.builder.CreateBitCast(keyAlloca, c.i8ptrType, "hashmap.keyptr")
|
||||||
c.builder.CreateStore(key, mapKeyAlloca)
|
params := []llvm.Value{m, keyPtr, mapValuePtr}
|
||||||
// Fetch the value from the hashmap.
|
|
||||||
params := []llvm.Value{m, mapKeyPtr, mapValuePtr}
|
|
||||||
commaOkValue = c.createRuntimeCall("hashmapBinaryGet", params, "")
|
commaOkValue = c.createRuntimeCall("hashmapBinaryGet", params, "")
|
||||||
c.builder.CreateCall(c.getLifetimeEndFunc(), []llvm.Value{mapKeySize, mapKeyPtr}, "")
|
|
||||||
} else {
|
} else {
|
||||||
// Not trivially comparable using memcmp.
|
|
||||||
return llvm.Value{}, c.makeError(pos, "only strings, bools, ints or structs of bools/ints are supported as map keys, but got: "+keyType.String())
|
return llvm.Value{}, c.makeError(pos, "only strings, bools, ints or structs of bools/ints are supported as map keys, but got: "+keyType.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load the resulting value from the hashmap. The value is set to the zero
|
|
||||||
// value if the key doesn't exist in the hashmap.
|
|
||||||
mapValue := c.builder.CreateLoad(mapValueAlloca, "")
|
mapValue := c.builder.CreateLoad(mapValueAlloca, "")
|
||||||
c.builder.CreateCall(c.getLifetimeEndFunc(), []llvm.Value{mapValueSize, mapValuePtr}, "")
|
|
||||||
|
|
||||||
if commaOk {
|
if commaOk {
|
||||||
tuple := llvm.Undef(c.ctx.StructType([]llvm.Type{llvmValueType, c.ctx.Int1Type()}, false))
|
tuple := llvm.Undef(c.ctx.StructType([]llvm.Type{llvmValueType, c.ctx.Int1Type()}, false))
|
||||||
tuple = c.builder.CreateInsertValue(tuple, mapValue, 0, "")
|
tuple = c.builder.CreateInsertValue(tuple, mapValue, 0, "")
|
||||||
@@ -54,8 +40,9 @@ func (c *Compiler) emitMapLookup(keyType, valueType types.Type, m, key llvm.Valu
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *Compiler) emitMapUpdate(keyType types.Type, m, key, value llvm.Value, pos token.Pos) {
|
func (c *Compiler) emitMapUpdate(keyType types.Type, m, key, value llvm.Value, pos token.Pos) {
|
||||||
valueAlloca, valuePtr, valueSize := c.createEntryBlockAlloca(value.Type(), "hashmap.value")
|
valueAlloca := c.builder.CreateAlloca(value.Type(), "hashmap.value")
|
||||||
c.builder.CreateStore(value, valueAlloca)
|
c.builder.CreateStore(value, valueAlloca)
|
||||||
|
valuePtr := c.builder.CreateBitCast(valueAlloca, c.i8ptrType, "hashmap.valueptr")
|
||||||
keyType = keyType.Underlying()
|
keyType = keyType.Underlying()
|
||||||
if t, ok := keyType.(*types.Basic); ok && t.Info()&types.IsString != 0 {
|
if t, ok := keyType.(*types.Basic); ok && t.Info()&types.IsString != 0 {
|
||||||
// key is a string
|
// key is a string
|
||||||
@@ -63,15 +50,14 @@ func (c *Compiler) emitMapUpdate(keyType types.Type, m, key, value llvm.Value, p
|
|||||||
c.createRuntimeCall("hashmapStringSet", params, "")
|
c.createRuntimeCall("hashmapStringSet", params, "")
|
||||||
} else if hashmapIsBinaryKey(keyType) {
|
} else if hashmapIsBinaryKey(keyType) {
|
||||||
// key can be compared with runtime.memequal
|
// key can be compared with runtime.memequal
|
||||||
keyAlloca, keyPtr, keySize := c.createEntryBlockAlloca(key.Type(), "hashmap.key")
|
keyAlloca := c.builder.CreateAlloca(key.Type(), "hashmap.key")
|
||||||
c.builder.CreateStore(key, keyAlloca)
|
c.builder.CreateStore(key, keyAlloca)
|
||||||
|
keyPtr := c.builder.CreateBitCast(keyAlloca, c.i8ptrType, "hashmap.keyptr")
|
||||||
params := []llvm.Value{m, keyPtr, valuePtr}
|
params := []llvm.Value{m, keyPtr, valuePtr}
|
||||||
c.createRuntimeCall("hashmapBinarySet", params, "")
|
c.createRuntimeCall("hashmapBinarySet", params, "")
|
||||||
c.builder.CreateCall(c.getLifetimeEndFunc(), []llvm.Value{keySize, keyPtr}, "")
|
|
||||||
} else {
|
} else {
|
||||||
c.addError(pos, "only strings, bools, ints or structs of bools/ints are supported as map keys, but got: "+keyType.String())
|
c.addError(pos, "only strings, bools, ints or structs of bools/ints are supported as map keys, but got: "+keyType.String())
|
||||||
}
|
}
|
||||||
c.builder.CreateCall(c.getLifetimeEndFunc(), []llvm.Value{valueSize, valuePtr}, "")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Compiler) emitMapDelete(keyType types.Type, m, key llvm.Value, pos token.Pos) error {
|
func (c *Compiler) emitMapDelete(keyType types.Type, m, key llvm.Value, pos token.Pos) error {
|
||||||
@@ -82,11 +68,11 @@ func (c *Compiler) emitMapDelete(keyType types.Type, m, key llvm.Value, pos toke
|
|||||||
c.createRuntimeCall("hashmapStringDelete", params, "")
|
c.createRuntimeCall("hashmapStringDelete", params, "")
|
||||||
return nil
|
return nil
|
||||||
} else if hashmapIsBinaryKey(keyType) {
|
} else if hashmapIsBinaryKey(keyType) {
|
||||||
keyAlloca, keyPtr, keySize := c.createEntryBlockAlloca(key.Type(), "hashmap.key")
|
keyAlloca := c.builder.CreateAlloca(key.Type(), "hashmap.key")
|
||||||
c.builder.CreateStore(key, keyAlloca)
|
c.builder.CreateStore(key, keyAlloca)
|
||||||
|
keyPtr := c.builder.CreateBitCast(keyAlloca, c.i8ptrType, "hashmap.keyptr")
|
||||||
params := []llvm.Value{m, keyPtr}
|
params := []llvm.Value{m, keyPtr}
|
||||||
c.createRuntimeCall("hashmapBinaryDelete", params, "")
|
c.createRuntimeCall("hashmapBinaryDelete", params, "")
|
||||||
c.builder.CreateCall(c.getLifetimeEndFunc(), []llvm.Value{keySize, keyPtr}, "")
|
|
||||||
return nil
|
return nil
|
||||||
} else {
|
} else {
|
||||||
return c.makeError(pos, "only strings, bools, ints or structs of bools/ints are supported as map keys, but got: "+keyType.String())
|
return c.makeError(pos, "only strings, bools, ints or structs of bools/ints are supported as map keys, but got: "+keyType.String())
|
||||||
|
|||||||
@@ -37,7 +37,6 @@ func (c *Compiler) Optimize(optLevel, sizeLevel int, inlinerThreshold uint) erro
|
|||||||
goPasses := llvm.NewPassManager()
|
goPasses := llvm.NewPassManager()
|
||||||
defer goPasses.Dispose()
|
defer goPasses.Dispose()
|
||||||
goPasses.AddGlobalOptimizerPass()
|
goPasses.AddGlobalOptimizerPass()
|
||||||
goPasses.AddGlobalDCEPass()
|
|
||||||
goPasses.AddConstantPropagationPass()
|
goPasses.AddConstantPropagationPass()
|
||||||
goPasses.AddAggressiveDCEPass()
|
goPasses.AddAggressiveDCEPass()
|
||||||
goPasses.AddFunctionAttrsPass()
|
goPasses.AddFunctionAttrsPass()
|
||||||
@@ -115,13 +114,6 @@ func (c *Compiler) Optimize(optLevel, sizeLevel int, inlinerThreshold uint) erro
|
|||||||
builder.Populate(modPasses)
|
builder.Populate(modPasses)
|
||||||
modPasses.Run(c.mod)
|
modPasses.Run(c.mod)
|
||||||
|
|
||||||
if c.gcIsPrecise() {
|
|
||||||
c.addGlobalsBitmap()
|
|
||||||
if err := c.Verify(); err != nil {
|
|
||||||
return errors.New("GC pass caused a verification failure")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,26 +0,0 @@
|
|||||||
package compiler
|
|
||||||
|
|
||||||
// This file implements volatile loads/stores in runtime/volatile.LoadT and
|
|
||||||
// runtime/volatile.StoreT as compiler builtins.
|
|
||||||
|
|
||||||
import (
|
|
||||||
"golang.org/x/tools/go/ssa"
|
|
||||||
"tinygo.org/x/go-llvm"
|
|
||||||
)
|
|
||||||
|
|
||||||
func (c *Compiler) emitVolatileLoad(frame *Frame, instr *ssa.CallCommon) (llvm.Value, error) {
|
|
||||||
addr := c.getValue(frame, instr.Args[0])
|
|
||||||
c.emitNilCheck(frame, addr, "deref")
|
|
||||||
val := c.builder.CreateLoad(addr, "")
|
|
||||||
val.SetVolatile(true)
|
|
||||||
return val, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Compiler) emitVolatileStore(frame *Frame, instr *ssa.CallCommon) (llvm.Value, error) {
|
|
||||||
addr := c.getValue(frame, instr.Args[0])
|
|
||||||
val := c.getValue(frame, instr.Args[1])
|
|
||||||
c.emitNilCheck(frame, addr, "deref")
|
|
||||||
store := c.builder.CreateStore(val, addr)
|
|
||||||
store.SetVolatile(true)
|
|
||||||
return llvm.Value{}, nil
|
|
||||||
}
|
|
||||||
@@ -25,7 +25,7 @@ func (c *Compiler) emitPointerPack(values []llvm.Value) llvm.Value {
|
|||||||
return llvm.ConstPointerNull(c.i8ptrType)
|
return llvm.ConstPointerNull(c.i8ptrType)
|
||||||
} else if len(values) == 1 && values[0].Type().TypeKind() == llvm.PointerTypeKind {
|
} else if len(values) == 1 && values[0].Type().TypeKind() == llvm.PointerTypeKind {
|
||||||
return c.builder.CreateBitCast(values[0], c.i8ptrType, "pack.ptr")
|
return c.builder.CreateBitCast(values[0], c.i8ptrType, "pack.ptr")
|
||||||
} else if size <= c.targetData.TypeAllocSize(c.i8ptrType) && !c.gcIsPrecise() {
|
} else if size <= c.targetData.TypeAllocSize(c.i8ptrType) {
|
||||||
// Packed data fits in a pointer, so store it directly inside the
|
// Packed data fits in a pointer, so store it directly inside the
|
||||||
// pointer.
|
// pointer.
|
||||||
if len(values) == 1 && values[0].Type().TypeKind() == llvm.IntegerTypeKind {
|
if len(values) == 1 && values[0].Type().TypeKind() == llvm.IntegerTypeKind {
|
||||||
@@ -57,7 +57,7 @@ func (c *Compiler) emitPointerPack(values []llvm.Value) llvm.Value {
|
|||||||
return c.builder.CreateLoad(packedAlloc, "")
|
return c.builder.CreateLoad(packedAlloc, "")
|
||||||
} else {
|
} else {
|
||||||
// Get the original heap allocation pointer, which already is an *i8.
|
// Get the original heap allocation pointer, which already is an *i8.
|
||||||
return c.builder.CreateBitCast(packedAlloc, c.i8ptrType, "")
|
return packedHeapAlloc
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -73,7 +73,7 @@ func (c *Compiler) emitPointerUnpack(ptr llvm.Value, valueTypes []llvm.Type) []l
|
|||||||
} else if len(valueTypes) == 1 && valueTypes[0].TypeKind() == llvm.PointerTypeKind {
|
} else if len(valueTypes) == 1 && valueTypes[0].TypeKind() == llvm.PointerTypeKind {
|
||||||
// A single pointer is always stored directly.
|
// A single pointer is always stored directly.
|
||||||
return []llvm.Value{c.builder.CreateBitCast(ptr, valueTypes[0], "unpack.ptr")}
|
return []llvm.Value{c.builder.CreateBitCast(ptr, valueTypes[0], "unpack.ptr")}
|
||||||
} else if size <= c.targetData.TypeAllocSize(c.i8ptrType) && !c.gcIsPrecise() {
|
} else if size <= c.targetData.TypeAllocSize(c.i8ptrType) {
|
||||||
// Packed data stored directly in pointer.
|
// Packed data stored directly in pointer.
|
||||||
if len(valueTypes) == 1 && valueTypes[0].TypeKind() == llvm.IntegerTypeKind {
|
if len(valueTypes) == 1 && valueTypes[0].TypeKind() == llvm.IntegerTypeKind {
|
||||||
// Keep this cast in SSA form.
|
// Keep this cast in SSA form.
|
||||||
|
|||||||
@@ -347,8 +347,6 @@ func (fr *frame) evalBasicBlock(bb, incoming llvm.BasicBlock, indent string) (re
|
|||||||
fr.locals[inst] = &LocalValue{fr.Eval, llvm.ConstInt(fr.Mod.Context().Int1Type(), implements, false)}
|
fr.locals[inst] = &LocalValue{fr.Eval, llvm.ConstInt(fr.Mod.Context().Int1Type(), implements, false)}
|
||||||
case callee.Name() == "runtime.nanotime":
|
case callee.Name() == "runtime.nanotime":
|
||||||
fr.locals[inst] = &LocalValue{fr.Eval, llvm.ConstInt(fr.Mod.Context().Int64Type(), 0, false)}
|
fr.locals[inst] = &LocalValue{fr.Eval, llvm.ConstInt(fr.Mod.Context().Int64Type(), 0, false)}
|
||||||
case callee.Name() == "llvm.dbg.value":
|
|
||||||
// do nothing
|
|
||||||
case strings.HasPrefix(callee.Name(), "runtime.print") || callee.Name() == "runtime._panic":
|
case strings.HasPrefix(callee.Name(), "runtime.print") || callee.Name() == "runtime._panic":
|
||||||
// This are all print instructions, which necessarily have side
|
// This are all print instructions, which necessarily have side
|
||||||
// effects but no results.
|
// effects but no results.
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ type Eval struct {
|
|||||||
TargetData llvm.TargetData
|
TargetData llvm.TargetData
|
||||||
Debug bool
|
Debug bool
|
||||||
builder llvm.Builder
|
builder llvm.Builder
|
||||||
|
dibuilder *llvm.DIBuilder
|
||||||
dirtyGlobals map[llvm.Value]struct{}
|
dirtyGlobals map[llvm.Value]struct{}
|
||||||
sideEffectFuncs map[llvm.Value]*sideEffectResult // cache of side effect scan results
|
sideEffectFuncs map[llvm.Value]*sideEffectResult // cache of side effect scan results
|
||||||
}
|
}
|
||||||
@@ -37,6 +38,7 @@ func Run(mod llvm.Module, targetData llvm.TargetData, debug bool) error {
|
|||||||
dirtyGlobals: map[llvm.Value]struct{}{},
|
dirtyGlobals: map[llvm.Value]struct{}{},
|
||||||
}
|
}
|
||||||
e.builder = mod.Context().NewBuilder()
|
e.builder = mod.Context().NewBuilder()
|
||||||
|
e.dibuilder = llvm.NewDIBuilder(mod)
|
||||||
|
|
||||||
initAll := mod.NamedFunction(name)
|
initAll := mod.NamedFunction(name)
|
||||||
bb := initAll.EntryBasicBlock()
|
bb := initAll.EntryBasicBlock()
|
||||||
@@ -47,6 +49,7 @@ func Run(mod llvm.Module, targetData llvm.TargetData, debug bool) error {
|
|||||||
e.builder.SetInsertPointBefore(bb.FirstInstruction())
|
e.builder.SetInsertPointBefore(bb.FirstInstruction())
|
||||||
dummy := e.builder.CreateAlloca(e.Mod.Context().Int8Type(), "dummy")
|
dummy := e.builder.CreateAlloca(e.Mod.Context().Int8Type(), "dummy")
|
||||||
e.builder.SetInsertPointBefore(dummy)
|
e.builder.SetInsertPointBefore(dummy)
|
||||||
|
e.builder.SetInstDebugLocation(bb.FirstInstruction())
|
||||||
var initCalls []llvm.Value
|
var initCalls []llvm.Value
|
||||||
for inst := bb.FirstInstruction(); !inst.IsNil(); inst = llvm.NextInstruction(inst) {
|
for inst := bb.FirstInstruction(); !inst.IsNil(); inst = llvm.NextInstruction(inst) {
|
||||||
if inst == dummy {
|
if inst == dummy {
|
||||||
|
|||||||
@@ -35,8 +35,6 @@ func (e *Eval) hasSideEffects(fn llvm.Value) *sideEffectResult {
|
|||||||
return &sideEffectResult{severity: sideEffectLimited}
|
return &sideEffectResult{severity: sideEffectLimited}
|
||||||
case "runtime.interfaceImplements":
|
case "runtime.interfaceImplements":
|
||||||
return &sideEffectResult{severity: sideEffectNone}
|
return &sideEffectResult{severity: sideEffectNone}
|
||||||
case "llvm.dbg.value":
|
|
||||||
return &sideEffectResult{severity: sideEffectNone}
|
|
||||||
}
|
}
|
||||||
if e.sideEffectFuncs == nil {
|
if e.sideEffectFuncs == nil {
|
||||||
e.sideEffectFuncs = make(map[llvm.Value]*sideEffectResult)
|
e.sideEffectFuncs = make(map[llvm.Value]*sideEffectResult)
|
||||||
|
|||||||
@@ -33,12 +33,11 @@ type Program struct {
|
|||||||
type Function struct {
|
type Function struct {
|
||||||
*ssa.Function
|
*ssa.Function
|
||||||
LLVMFn llvm.Value
|
LLVMFn llvm.Value
|
||||||
linkName string // go:linkname, go:export, go:interrupt
|
linkName string // go:linkname, go:export, go:interrupt
|
||||||
exported bool // go:export
|
exported bool // go:export
|
||||||
nobounds bool // go:nobounds
|
nobounds bool // go:nobounds
|
||||||
flag bool // used by dead code elimination
|
flag bool // used by dead code elimination
|
||||||
interrupt bool // go:interrupt
|
interrupt bool // go:interrupt
|
||||||
inline InlineType // go:inline
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Global variable, possibly constant.
|
// Global variable, possibly constant.
|
||||||
@@ -70,22 +69,7 @@ type Interface struct {
|
|||||||
Type *types.Interface
|
Type *types.Interface
|
||||||
}
|
}
|
||||||
|
|
||||||
type InlineType int
|
// Create and intialize a new *Program from a *ssa.Program.
|
||||||
|
|
||||||
// How much to inline.
|
|
||||||
const (
|
|
||||||
// Default behavior. The compiler decides for itself whether any given
|
|
||||||
// function will be inlined. Whether any function is inlined depends on the
|
|
||||||
// optimization level.
|
|
||||||
InlineDefault InlineType = iota
|
|
||||||
|
|
||||||
// Inline hint, just like the C inline keyword (signalled using
|
|
||||||
// //go:inline). The compiler will be more likely to inline this function,
|
|
||||||
// but it is not a guarantee.
|
|
||||||
InlineHint
|
|
||||||
)
|
|
||||||
|
|
||||||
// Create and initialize a new *Program from a *ssa.Program.
|
|
||||||
func NewProgram(lprogram *loader.Program, mainPath string) *Program {
|
func NewProgram(lprogram *loader.Program, mainPath string) *Program {
|
||||||
comments := map[string]*ast.CommentGroup{}
|
comments := map[string]*ast.CommentGroup{}
|
||||||
for _, pkgInfo := range lprogram.Sorted() {
|
for _, pkgInfo := range lprogram.Sorted() {
|
||||||
@@ -295,8 +279,6 @@ func (f *Function) parsePragmas() {
|
|||||||
}
|
}
|
||||||
f.linkName = parts[1]
|
f.linkName = parts[1]
|
||||||
f.exported = true
|
f.exported = true
|
||||||
case "//go:inline":
|
|
||||||
f.inline = InlineHint
|
|
||||||
case "//go:interrupt":
|
case "//go:interrupt":
|
||||||
if len(parts) != 2 {
|
if len(parts) != 2 {
|
||||||
continue
|
continue
|
||||||
@@ -350,11 +332,6 @@ func (f *Function) IsInterrupt() bool {
|
|||||||
return f.interrupt
|
return f.interrupt
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return the inline directive of this function.
|
|
||||||
func (f *Function) Inline() InlineType {
|
|
||||||
return f.inline
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return the link name for this function.
|
// Return the link name for this function.
|
||||||
func (f *Function) LinkName() string {
|
func (f *Function) LinkName() string {
|
||||||
if f.linkName != "" {
|
if f.linkName != "" {
|
||||||
|
|||||||
+137
-252
@@ -1,15 +1,7 @@
|
|||||||
// Package cgo implements CGo by modifying a loaded AST. It does this by parsing
|
package loader
|
||||||
// the `import "C"` statements found in the source code with libclang and
|
|
||||||
// generating stub function and global declarations.
|
|
||||||
//
|
|
||||||
// There are a few advantages to modifying the AST directly instead of doing CGo
|
|
||||||
// as a preprocessing step, with the main advantage being that debug information
|
|
||||||
// is kept intact as much as possible.
|
|
||||||
package cgo
|
|
||||||
|
|
||||||
// This file extracts the `import "C"` statement from the source and modifies
|
// This file extracts the `import "C"` statement from the source and modifies
|
||||||
// the AST for CGo. It does not use libclang directly: see libclang.go for the C
|
// the AST for Cgo. It does not use libclang directly (see libclang.go).
|
||||||
// source file parsing.
|
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"go/ast"
|
"go/ast"
|
||||||
@@ -21,38 +13,27 @@ import (
|
|||||||
"golang.org/x/tools/go/ast/astutil"
|
"golang.org/x/tools/go/ast/astutil"
|
||||||
)
|
)
|
||||||
|
|
||||||
// cgoPackage holds all CGo-related information of a package.
|
// fileInfo holds all Cgo-related information of a given *ast.File.
|
||||||
type cgoPackage struct {
|
type fileInfo struct {
|
||||||
generated *ast.File
|
*ast.File
|
||||||
generatedPos token.Pos
|
*Package
|
||||||
errors []error
|
filename string
|
||||||
dir string
|
|
||||||
fset *token.FileSet
|
|
||||||
tokenFiles map[string]*token.File
|
|
||||||
missingSymbols map[string]struct{}
|
|
||||||
constants map[string]constantInfo
|
|
||||||
functions map[string]*functionInfo
|
functions map[string]*functionInfo
|
||||||
globals map[string]globalInfo
|
globals map[string]*globalInfo
|
||||||
typedefs map[string]*typedefInfo
|
typedefs map[string]*typedefInfo
|
||||||
elaboratedTypes map[string]*elaboratedTypeInfo
|
elaboratedTypes map[string]ast.Expr
|
||||||
|
importCPos token.Pos
|
||||||
|
missingSymbols map[string]struct{}
|
||||||
}
|
}
|
||||||
|
|
||||||
// constantInfo stores some information about a CGo constant found by libclang
|
// functionInfo stores some information about a Cgo function found by libclang
|
||||||
// and declared in the Go AST.
|
|
||||||
type constantInfo struct {
|
|
||||||
expr *ast.BasicLit
|
|
||||||
pos token.Pos
|
|
||||||
}
|
|
||||||
|
|
||||||
// functionInfo stores some information about a CGo function found by libclang
|
|
||||||
// and declared in the AST.
|
// and declared in the AST.
|
||||||
type functionInfo struct {
|
type functionInfo struct {
|
||||||
args []paramInfo
|
args []paramInfo
|
||||||
results *ast.FieldList
|
results *ast.FieldList
|
||||||
pos token.Pos
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// paramInfo is a parameter of a CGo function (see functionInfo).
|
// paramInfo is a parameter of a Cgo function (see functionInfo).
|
||||||
type paramInfo struct {
|
type paramInfo struct {
|
||||||
name string
|
name string
|
||||||
typeExpr ast.Expr
|
typeExpr ast.Expr
|
||||||
@@ -61,20 +42,11 @@ type paramInfo struct {
|
|||||||
// typedefInfo contains information about a single typedef in C.
|
// typedefInfo contains information about a single typedef in C.
|
||||||
type typedefInfo struct {
|
type typedefInfo struct {
|
||||||
typeExpr ast.Expr
|
typeExpr ast.Expr
|
||||||
pos token.Pos
|
|
||||||
}
|
|
||||||
|
|
||||||
// elaboratedTypeInfo contains some information about an elaborated type
|
|
||||||
// (struct, union) found in the C AST.
|
|
||||||
type elaboratedTypeInfo struct {
|
|
||||||
typeExpr ast.Expr
|
|
||||||
pos token.Pos
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// globalInfo contains information about a declared global variable in C.
|
// globalInfo contains information about a declared global variable in C.
|
||||||
type globalInfo struct {
|
type globalInfo struct {
|
||||||
typeExpr ast.Expr
|
typeExpr ast.Expr
|
||||||
pos token.Pos
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// cgoAliases list type aliases between Go and C, for types that are equivalent
|
// cgoAliases list type aliases between Go and C, for types that are equivalent
|
||||||
@@ -91,9 +63,9 @@ var cgoAliases = map[string]string{
|
|||||||
"C.uintptr_t": "uintptr",
|
"C.uintptr_t": "uintptr",
|
||||||
}
|
}
|
||||||
|
|
||||||
// builtinAliases are handled specially because they only exist on the Go side
|
// cgoBuiltinAliases are handled specially because they only exist on the Go
|
||||||
// of CGo, not on the CGo side (they're prefixed with "_Cgo_" there).
|
// side of CGo, not on the CGo (they're prefixed with "_Cgo_" there).
|
||||||
var builtinAliases = map[string]struct{}{
|
var cgoBuiltinAliases = map[string]struct{}{
|
||||||
"char": struct{}{},
|
"char": struct{}{},
|
||||||
"schar": struct{}{},
|
"schar": struct{}{},
|
||||||
"uchar": struct{}{},
|
"uchar": struct{}{},
|
||||||
@@ -124,146 +96,103 @@ typedef long long _Cgo_longlong;
|
|||||||
typedef unsigned long long _Cgo_ulonglong;
|
typedef unsigned long long _Cgo_ulonglong;
|
||||||
`
|
`
|
||||||
|
|
||||||
// Process extracts `import "C"` statements from the AST, parses the comment
|
// processCgo extracts the `import "C"` statement from the AST, parses the
|
||||||
// with libclang, and modifies the AST to use this information. It returns a
|
// comment with libclang, and modifies the AST to use this information.
|
||||||
// newly created *ast.File that should be added to the list of to-be-parsed
|
func (p *Package) processCgo(filename string, f *ast.File, cflags []string) []error {
|
||||||
// files. If there is one or more error, it returns these in the []error slice
|
info := &fileInfo{
|
||||||
// but still modifies the AST.
|
File: f,
|
||||||
func Process(files []*ast.File, dir string, fset *token.FileSet, cflags []string) (*ast.File, []error) {
|
Package: p,
|
||||||
p := &cgoPackage{
|
filename: filename,
|
||||||
dir: dir,
|
|
||||||
fset: fset,
|
|
||||||
tokenFiles: map[string]*token.File{},
|
|
||||||
missingSymbols: map[string]struct{}{},
|
|
||||||
constants: map[string]constantInfo{},
|
|
||||||
functions: map[string]*functionInfo{},
|
functions: map[string]*functionInfo{},
|
||||||
globals: map[string]globalInfo{},
|
globals: map[string]*globalInfo{},
|
||||||
typedefs: map[string]*typedefInfo{},
|
typedefs: map[string]*typedefInfo{},
|
||||||
elaboratedTypes: map[string]*elaboratedTypeInfo{},
|
elaboratedTypes: map[string]ast.Expr{},
|
||||||
}
|
missingSymbols: map[string]struct{}{},
|
||||||
|
|
||||||
// Add a new location for the following file.
|
|
||||||
generatedTokenPos := p.fset.AddFile(dir+"/!cgo.go", -1, 0)
|
|
||||||
generatedTokenPos.SetLines([]int{0})
|
|
||||||
p.generatedPos = generatedTokenPos.Pos(0)
|
|
||||||
|
|
||||||
// Construct a new in-memory AST for CGo declarations of this package.
|
|
||||||
unsafeImport := &ast.ImportSpec{
|
|
||||||
Path: &ast.BasicLit{
|
|
||||||
ValuePos: p.generatedPos,
|
|
||||||
Kind: token.STRING,
|
|
||||||
Value: "\"unsafe\"",
|
|
||||||
},
|
|
||||||
EndPos: p.generatedPos,
|
|
||||||
}
|
|
||||||
p.generated = &ast.File{
|
|
||||||
Package: p.generatedPos,
|
|
||||||
Name: &ast.Ident{
|
|
||||||
NamePos: p.generatedPos,
|
|
||||||
Name: files[0].Name.Name,
|
|
||||||
},
|
|
||||||
Decls: []ast.Decl{
|
|
||||||
&ast.GenDecl{
|
|
||||||
TokPos: p.generatedPos,
|
|
||||||
Tok: token.IMPORT,
|
|
||||||
Specs: []ast.Spec{
|
|
||||||
unsafeImport,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Imports: []*ast.ImportSpec{unsafeImport},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Find all C.* symbols.
|
// Find all C.* symbols.
|
||||||
for _, f := range files {
|
f = astutil.Apply(f, info.findMissingCGoNames, nil).(*ast.File)
|
||||||
astutil.Apply(f, p.findMissingCGoNames, nil)
|
for name := range cgoBuiltinAliases {
|
||||||
}
|
info.missingSymbols["_Cgo_"+name] = struct{}{}
|
||||||
for name := range builtinAliases {
|
|
||||||
p.missingSymbols["_Cgo_"+name] = struct{}{}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Find `import "C"` statements in the file.
|
// Find `import "C"` statements in the file.
|
||||||
for _, f := range files {
|
for i := 0; i < len(f.Decls); i++ {
|
||||||
for i := 0; i < len(f.Decls); i++ {
|
decl := f.Decls[i]
|
||||||
decl := f.Decls[i]
|
genDecl, ok := decl.(*ast.GenDecl)
|
||||||
genDecl, ok := decl.(*ast.GenDecl)
|
if !ok {
|
||||||
if !ok {
|
continue
|
||||||
continue
|
}
|
||||||
}
|
if len(genDecl.Specs) != 1 {
|
||||||
if len(genDecl.Specs) != 1 {
|
continue
|
||||||
continue
|
}
|
||||||
}
|
spec, ok := genDecl.Specs[0].(*ast.ImportSpec)
|
||||||
spec, ok := genDecl.Specs[0].(*ast.ImportSpec)
|
if !ok {
|
||||||
if !ok {
|
continue
|
||||||
continue
|
}
|
||||||
}
|
path, err := strconv.Unquote(spec.Path.Value)
|
||||||
path, err := strconv.Unquote(spec.Path.Value)
|
if err != nil {
|
||||||
if err != nil {
|
panic("could not parse import path: " + err.Error())
|
||||||
panic("could not parse import path: " + err.Error())
|
}
|
||||||
}
|
if path != "C" {
|
||||||
if path != "C" {
|
continue
|
||||||
continue
|
}
|
||||||
}
|
cgoComment := genDecl.Doc.Text()
|
||||||
cgoComment := genDecl.Doc.Text()
|
|
||||||
|
|
||||||
pos := genDecl.Pos()
|
// Stored for later use by generated functions, to use a somewhat sane
|
||||||
if genDecl.Doc != nil {
|
// source location.
|
||||||
pos = genDecl.Doc.Pos()
|
info.importCPos = spec.Path.ValuePos
|
||||||
}
|
|
||||||
position := fset.PositionFor(pos, true)
|
|
||||||
p.parseFragment(cgoComment+cgoTypes, cflags, position.Filename, position.Line)
|
|
||||||
|
|
||||||
// Remove this import declaration.
|
pos := info.fset.PositionFor(genDecl.Doc.Pos(), true)
|
||||||
f.Decls = append(f.Decls[:i], f.Decls[i+1:]...)
|
errs := info.parseFragment(cgoComment+cgoTypes, cflags, pos.Filename, pos.Line)
|
||||||
i--
|
if errs != nil {
|
||||||
|
return errs
|
||||||
}
|
}
|
||||||
|
|
||||||
// Print the AST, for debugging.
|
// Remove this import declaration.
|
||||||
//ast.Print(fset, f)
|
f.Decls = append(f.Decls[:i], f.Decls[i+1:]...)
|
||||||
|
i--
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Print the AST, for debugging.
|
||||||
|
//ast.Print(p.fset, f)
|
||||||
|
|
||||||
// Declare functions found by libclang.
|
// Declare functions found by libclang.
|
||||||
p.addFuncDecls()
|
info.addFuncDecls()
|
||||||
|
|
||||||
// Declare stub function pointer values found by libclang.
|
// Declare stub function pointer values found by libclang.
|
||||||
p.addFuncPtrDecls()
|
info.addFuncPtrDecls()
|
||||||
|
|
||||||
// Declare globals found by libclang.
|
// Declare globals found by libclang.
|
||||||
p.addConstDecls()
|
info.addVarDecls()
|
||||||
|
|
||||||
// Declare globals found by libclang.
|
|
||||||
p.addVarDecls()
|
|
||||||
|
|
||||||
// Forward C types to Go types (like C.uint32_t -> uint32).
|
// Forward C types to Go types (like C.uint32_t -> uint32).
|
||||||
p.addTypeAliases()
|
info.addTypeAliases()
|
||||||
|
|
||||||
// Add type declarations for C types, declared using typedef in C.
|
// Add type declarations for C types, declared using typedef in C.
|
||||||
p.addTypedefs()
|
info.addTypedefs()
|
||||||
|
|
||||||
// Add elaborated types for C structs and unions.
|
// Add elaborated types for C structs and unions.
|
||||||
p.addElaboratedTypes()
|
info.addElaboratedTypes()
|
||||||
|
|
||||||
// Patch the AST to use the declared types and functions.
|
// Patch the AST to use the declared types and functions.
|
||||||
for _, f := range files {
|
f = astutil.Apply(f, info.walker, nil).(*ast.File)
|
||||||
astutil.Apply(f, p.walker, nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Print the newly generated in-memory AST, for debugging.
|
return nil
|
||||||
//ast.Print(fset, p.generated)
|
|
||||||
|
|
||||||
return p.generated, p.errors
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// addFuncDecls adds the C function declarations found by libclang in the
|
// addFuncDecls adds the C function declarations found by libclang in the
|
||||||
// comment above the `import "C"` statement.
|
// comment above the `import "C"` statement.
|
||||||
func (p *cgoPackage) addFuncDecls() {
|
func (info *fileInfo) addFuncDecls() {
|
||||||
names := make([]string, 0, len(p.functions))
|
// TODO: replace all uses of importCPos with the real locations from
|
||||||
for name := range p.functions {
|
// libclang.
|
||||||
|
names := make([]string, 0, len(info.functions))
|
||||||
|
for name := range info.functions {
|
||||||
names = append(names, name)
|
names = append(names, name)
|
||||||
}
|
}
|
||||||
sort.Strings(names)
|
sort.Strings(names)
|
||||||
for _, name := range names {
|
for _, name := range names {
|
||||||
fn := p.functions[name]
|
fn := info.functions[name]
|
||||||
obj := &ast.Object{
|
obj := &ast.Object{
|
||||||
Kind: ast.Fun,
|
Kind: ast.Fun,
|
||||||
Name: "C." + name,
|
Name: "C." + name,
|
||||||
@@ -271,16 +200,16 @@ func (p *cgoPackage) addFuncDecls() {
|
|||||||
args := make([]*ast.Field, len(fn.args))
|
args := make([]*ast.Field, len(fn.args))
|
||||||
decl := &ast.FuncDecl{
|
decl := &ast.FuncDecl{
|
||||||
Name: &ast.Ident{
|
Name: &ast.Ident{
|
||||||
NamePos: fn.pos,
|
NamePos: info.importCPos,
|
||||||
Name: "C." + name,
|
Name: "C." + name,
|
||||||
Obj: obj,
|
Obj: obj,
|
||||||
},
|
},
|
||||||
Type: &ast.FuncType{
|
Type: &ast.FuncType{
|
||||||
Func: fn.pos,
|
Func: info.importCPos,
|
||||||
Params: &ast.FieldList{
|
Params: &ast.FieldList{
|
||||||
Opening: fn.pos,
|
Opening: info.importCPos,
|
||||||
List: args,
|
List: args,
|
||||||
Closing: fn.pos,
|
Closing: info.importCPos,
|
||||||
},
|
},
|
||||||
Results: fn.results,
|
Results: fn.results,
|
||||||
},
|
},
|
||||||
@@ -290,7 +219,7 @@ func (p *cgoPackage) addFuncDecls() {
|
|||||||
args[i] = &ast.Field{
|
args[i] = &ast.Field{
|
||||||
Names: []*ast.Ident{
|
Names: []*ast.Ident{
|
||||||
&ast.Ident{
|
&ast.Ident{
|
||||||
NamePos: fn.pos,
|
NamePos: info.importCPos,
|
||||||
Name: arg.name,
|
Name: arg.name,
|
||||||
Obj: &ast.Object{
|
Obj: &ast.Object{
|
||||||
Kind: ast.Var,
|
Kind: ast.Var,
|
||||||
@@ -302,7 +231,7 @@ func (p *cgoPackage) addFuncDecls() {
|
|||||||
Type: arg.typeExpr,
|
Type: arg.typeExpr,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
p.generated.Decls = append(p.generated.Decls, decl)
|
info.Decls = append(info.Decls, decl)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -315,40 +244,39 @@ func (p *cgoPackage) addFuncDecls() {
|
|||||||
// C.mul unsafe.Pointer
|
// C.mul unsafe.Pointer
|
||||||
// // ...
|
// // ...
|
||||||
// )
|
// )
|
||||||
func (p *cgoPackage) addFuncPtrDecls() {
|
func (info *fileInfo) addFuncPtrDecls() {
|
||||||
if len(p.functions) == 0 {
|
if len(info.functions) == 0 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
gen := &ast.GenDecl{
|
gen := &ast.GenDecl{
|
||||||
TokPos: token.NoPos,
|
TokPos: info.importCPos,
|
||||||
Tok: token.VAR,
|
Tok: token.VAR,
|
||||||
Lparen: token.NoPos,
|
Lparen: info.importCPos,
|
||||||
Rparen: token.NoPos,
|
Rparen: info.importCPos,
|
||||||
}
|
}
|
||||||
names := make([]string, 0, len(p.functions))
|
names := make([]string, 0, len(info.functions))
|
||||||
for name := range p.functions {
|
for name := range info.functions {
|
||||||
names = append(names, name)
|
names = append(names, name)
|
||||||
}
|
}
|
||||||
sort.Strings(names)
|
sort.Strings(names)
|
||||||
for _, name := range names {
|
for _, name := range names {
|
||||||
fn := p.functions[name]
|
|
||||||
obj := &ast.Object{
|
obj := &ast.Object{
|
||||||
Kind: ast.Typ,
|
Kind: ast.Typ,
|
||||||
Name: "C." + name + "$funcaddr",
|
Name: "C." + name + "$funcaddr",
|
||||||
}
|
}
|
||||||
valueSpec := &ast.ValueSpec{
|
valueSpec := &ast.ValueSpec{
|
||||||
Names: []*ast.Ident{&ast.Ident{
|
Names: []*ast.Ident{&ast.Ident{
|
||||||
NamePos: fn.pos,
|
NamePos: info.importCPos,
|
||||||
Name: "C." + name + "$funcaddr",
|
Name: "C." + name + "$funcaddr",
|
||||||
Obj: obj,
|
Obj: obj,
|
||||||
}},
|
}},
|
||||||
Type: &ast.SelectorExpr{
|
Type: &ast.SelectorExpr{
|
||||||
X: &ast.Ident{
|
X: &ast.Ident{
|
||||||
NamePos: fn.pos,
|
NamePos: info.importCPos,
|
||||||
Name: "unsafe",
|
Name: "unsafe",
|
||||||
},
|
},
|
||||||
Sel: &ast.Ident{
|
Sel: &ast.Ident{
|
||||||
NamePos: fn.pos,
|
NamePos: info.importCPos,
|
||||||
Name: "Pointer",
|
Name: "Pointer",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -356,50 +284,7 @@ func (p *cgoPackage) addFuncPtrDecls() {
|
|||||||
obj.Decl = valueSpec
|
obj.Decl = valueSpec
|
||||||
gen.Specs = append(gen.Specs, valueSpec)
|
gen.Specs = append(gen.Specs, valueSpec)
|
||||||
}
|
}
|
||||||
p.generated.Decls = append(p.generated.Decls, gen)
|
info.Decls = append(info.Decls, gen)
|
||||||
}
|
|
||||||
|
|
||||||
// addConstDecls declares external C constants in the Go source.
|
|
||||||
// It adds code like the following to the AST:
|
|
||||||
//
|
|
||||||
// const (
|
|
||||||
// C.CONST_INT = 5
|
|
||||||
// C.CONST_FLOAT = 5.8
|
|
||||||
// // ...
|
|
||||||
// )
|
|
||||||
func (p *cgoPackage) addConstDecls() {
|
|
||||||
if len(p.constants) == 0 {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
gen := &ast.GenDecl{
|
|
||||||
TokPos: token.NoPos,
|
|
||||||
Tok: token.CONST,
|
|
||||||
Lparen: token.NoPos,
|
|
||||||
Rparen: token.NoPos,
|
|
||||||
}
|
|
||||||
names := make([]string, 0, len(p.constants))
|
|
||||||
for name := range p.constants {
|
|
||||||
names = append(names, name)
|
|
||||||
}
|
|
||||||
sort.Strings(names)
|
|
||||||
for _, name := range names {
|
|
||||||
constVal := p.constants[name]
|
|
||||||
obj := &ast.Object{
|
|
||||||
Kind: ast.Con,
|
|
||||||
Name: "C." + name,
|
|
||||||
}
|
|
||||||
valueSpec := &ast.ValueSpec{
|
|
||||||
Names: []*ast.Ident{&ast.Ident{
|
|
||||||
NamePos: constVal.pos,
|
|
||||||
Name: "C." + name,
|
|
||||||
Obj: obj,
|
|
||||||
}},
|
|
||||||
Values: []ast.Expr{constVal.expr},
|
|
||||||
}
|
|
||||||
obj.Decl = valueSpec
|
|
||||||
gen.Specs = append(gen.Specs, valueSpec)
|
|
||||||
}
|
|
||||||
p.generated.Decls = append(p.generated.Decls, gen)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// addVarDecls declares external C globals in the Go source.
|
// addVarDecls declares external C globals in the Go source.
|
||||||
@@ -410,30 +295,30 @@ func (p *cgoPackage) addConstDecls() {
|
|||||||
// C.globalBool bool
|
// C.globalBool bool
|
||||||
// // ...
|
// // ...
|
||||||
// )
|
// )
|
||||||
func (p *cgoPackage) addVarDecls() {
|
func (info *fileInfo) addVarDecls() {
|
||||||
if len(p.globals) == 0 {
|
if len(info.globals) == 0 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
gen := &ast.GenDecl{
|
gen := &ast.GenDecl{
|
||||||
TokPos: token.NoPos,
|
TokPos: info.importCPos,
|
||||||
Tok: token.VAR,
|
Tok: token.VAR,
|
||||||
Lparen: token.NoPos,
|
Lparen: info.importCPos,
|
||||||
Rparen: token.NoPos,
|
Rparen: info.importCPos,
|
||||||
}
|
}
|
||||||
names := make([]string, 0, len(p.globals))
|
names := make([]string, 0, len(info.globals))
|
||||||
for name := range p.globals {
|
for name := range info.globals {
|
||||||
names = append(names, name)
|
names = append(names, name)
|
||||||
}
|
}
|
||||||
sort.Strings(names)
|
sort.Strings(names)
|
||||||
for _, name := range names {
|
for _, name := range names {
|
||||||
global := p.globals[name]
|
global := info.globals[name]
|
||||||
obj := &ast.Object{
|
obj := &ast.Object{
|
||||||
Kind: ast.Var,
|
Kind: ast.Typ,
|
||||||
Name: "C." + name,
|
Name: "C." + name,
|
||||||
}
|
}
|
||||||
valueSpec := &ast.ValueSpec{
|
valueSpec := &ast.ValueSpec{
|
||||||
Names: []*ast.Ident{&ast.Ident{
|
Names: []*ast.Ident{&ast.Ident{
|
||||||
NamePos: global.pos,
|
NamePos: info.importCPos,
|
||||||
Name: "C." + name,
|
Name: "C." + name,
|
||||||
Obj: obj,
|
Obj: obj,
|
||||||
}},
|
}},
|
||||||
@@ -442,7 +327,7 @@ func (p *cgoPackage) addVarDecls() {
|
|||||||
obj.Decl = valueSpec
|
obj.Decl = valueSpec
|
||||||
gen.Specs = append(gen.Specs, valueSpec)
|
gen.Specs = append(gen.Specs, valueSpec)
|
||||||
}
|
}
|
||||||
p.generated.Decls = append(p.generated.Decls, gen)
|
info.Decls = append(info.Decls, gen)
|
||||||
}
|
}
|
||||||
|
|
||||||
// addTypeAliases aliases some built-in Go types with their equivalent C types.
|
// addTypeAliases aliases some built-in Go types with their equivalent C types.
|
||||||
@@ -453,17 +338,17 @@ func (p *cgoPackage) addVarDecls() {
|
|||||||
// C.int16_t = int16
|
// C.int16_t = int16
|
||||||
// // ...
|
// // ...
|
||||||
// )
|
// )
|
||||||
func (p *cgoPackage) addTypeAliases() {
|
func (info *fileInfo) addTypeAliases() {
|
||||||
aliasKeys := make([]string, 0, len(cgoAliases))
|
aliasKeys := make([]string, 0, len(cgoAliases))
|
||||||
for key := range cgoAliases {
|
for key := range cgoAliases {
|
||||||
aliasKeys = append(aliasKeys, key)
|
aliasKeys = append(aliasKeys, key)
|
||||||
}
|
}
|
||||||
sort.Strings(aliasKeys)
|
sort.Strings(aliasKeys)
|
||||||
gen := &ast.GenDecl{
|
gen := &ast.GenDecl{
|
||||||
TokPos: token.NoPos,
|
TokPos: info.importCPos,
|
||||||
Tok: token.TYPE,
|
Tok: token.TYPE,
|
||||||
Lparen: token.NoPos,
|
Lparen: info.importCPos,
|
||||||
Rparen: token.NoPos,
|
Rparen: info.importCPos,
|
||||||
}
|
}
|
||||||
for _, typeName := range aliasKeys {
|
for _, typeName := range aliasKeys {
|
||||||
goTypeName := cgoAliases[typeName]
|
goTypeName := cgoAliases[typeName]
|
||||||
@@ -473,37 +358,37 @@ func (p *cgoPackage) addTypeAliases() {
|
|||||||
}
|
}
|
||||||
typeSpec := &ast.TypeSpec{
|
typeSpec := &ast.TypeSpec{
|
||||||
Name: &ast.Ident{
|
Name: &ast.Ident{
|
||||||
NamePos: token.NoPos,
|
NamePos: info.importCPos,
|
||||||
Name: typeName,
|
Name: typeName,
|
||||||
Obj: obj,
|
Obj: obj,
|
||||||
},
|
},
|
||||||
Assign: p.generatedPos,
|
Assign: info.importCPos,
|
||||||
Type: &ast.Ident{
|
Type: &ast.Ident{
|
||||||
NamePos: token.NoPos,
|
NamePos: info.importCPos,
|
||||||
Name: goTypeName,
|
Name: goTypeName,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
obj.Decl = typeSpec
|
obj.Decl = typeSpec
|
||||||
gen.Specs = append(gen.Specs, typeSpec)
|
gen.Specs = append(gen.Specs, typeSpec)
|
||||||
}
|
}
|
||||||
p.generated.Decls = append(p.generated.Decls, gen)
|
info.Decls = append(info.Decls, gen)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *cgoPackage) addTypedefs() {
|
func (info *fileInfo) addTypedefs() {
|
||||||
if len(p.typedefs) == 0 {
|
if len(info.typedefs) == 0 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
gen := &ast.GenDecl{
|
gen := &ast.GenDecl{
|
||||||
TokPos: token.NoPos,
|
TokPos: info.importCPos,
|
||||||
Tok: token.TYPE,
|
Tok: token.TYPE,
|
||||||
}
|
}
|
||||||
names := make([]string, 0, len(p.typedefs))
|
names := make([]string, 0, len(info.typedefs))
|
||||||
for name := range p.typedefs {
|
for name := range info.typedefs {
|
||||||
names = append(names, name)
|
names = append(names, name)
|
||||||
}
|
}
|
||||||
sort.Strings(names)
|
sort.Strings(names)
|
||||||
for _, name := range names {
|
for _, name := range names {
|
||||||
typedef := p.typedefs[name]
|
typedef := info.typedefs[name]
|
||||||
typeName := "C." + name
|
typeName := "C." + name
|
||||||
isAlias := true
|
isAlias := true
|
||||||
if strings.HasPrefix(name, "_Cgo_") {
|
if strings.HasPrefix(name, "_Cgo_") {
|
||||||
@@ -520,19 +405,19 @@ func (p *cgoPackage) addTypedefs() {
|
|||||||
}
|
}
|
||||||
typeSpec := &ast.TypeSpec{
|
typeSpec := &ast.TypeSpec{
|
||||||
Name: &ast.Ident{
|
Name: &ast.Ident{
|
||||||
NamePos: typedef.pos,
|
NamePos: info.importCPos,
|
||||||
Name: typeName,
|
Name: typeName,
|
||||||
Obj: obj,
|
Obj: obj,
|
||||||
},
|
},
|
||||||
Type: typedef.typeExpr,
|
Type: typedef.typeExpr,
|
||||||
}
|
}
|
||||||
if isAlias {
|
if isAlias {
|
||||||
typeSpec.Assign = typedef.pos
|
typeSpec.Assign = info.importCPos
|
||||||
}
|
}
|
||||||
obj.Decl = typeSpec
|
obj.Decl = typeSpec
|
||||||
gen.Specs = append(gen.Specs, typeSpec)
|
gen.Specs = append(gen.Specs, typeSpec)
|
||||||
}
|
}
|
||||||
p.generated.Decls = append(p.generated.Decls, gen)
|
info.Decls = append(info.Decls, gen)
|
||||||
}
|
}
|
||||||
|
|
||||||
// addElaboratedTypes adds C elaborated types as aliases. These are the "struct
|
// addElaboratedTypes adds C elaborated types as aliases. These are the "struct
|
||||||
@@ -540,21 +425,21 @@ func (p *cgoPackage) addTypedefs() {
|
|||||||
//
|
//
|
||||||
// See also:
|
// See also:
|
||||||
// https://en.cppreference.com/w/cpp/language/elaborated_type_specifier
|
// https://en.cppreference.com/w/cpp/language/elaborated_type_specifier
|
||||||
func (p *cgoPackage) addElaboratedTypes() {
|
func (info *fileInfo) addElaboratedTypes() {
|
||||||
if len(p.elaboratedTypes) == 0 {
|
if len(info.elaboratedTypes) == 0 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
gen := &ast.GenDecl{
|
gen := &ast.GenDecl{
|
||||||
TokPos: token.NoPos,
|
TokPos: info.importCPos,
|
||||||
Tok: token.TYPE,
|
Tok: token.TYPE,
|
||||||
}
|
}
|
||||||
names := make([]string, 0, len(p.elaboratedTypes))
|
names := make([]string, 0, len(info.elaboratedTypes))
|
||||||
for name := range p.elaboratedTypes {
|
for name := range info.elaboratedTypes {
|
||||||
names = append(names, name)
|
names = append(names, name)
|
||||||
}
|
}
|
||||||
sort.Strings(names)
|
sort.Strings(names)
|
||||||
for _, name := range names {
|
for _, name := range names {
|
||||||
typ := p.elaboratedTypes[name]
|
typ := info.elaboratedTypes[name]
|
||||||
typeName := "C." + name
|
typeName := "C." + name
|
||||||
obj := &ast.Object{
|
obj := &ast.Object{
|
||||||
Kind: ast.Typ,
|
Kind: ast.Typ,
|
||||||
@@ -562,22 +447,22 @@ func (p *cgoPackage) addElaboratedTypes() {
|
|||||||
}
|
}
|
||||||
typeSpec := &ast.TypeSpec{
|
typeSpec := &ast.TypeSpec{
|
||||||
Name: &ast.Ident{
|
Name: &ast.Ident{
|
||||||
NamePos: typ.pos,
|
NamePos: info.importCPos,
|
||||||
Name: typeName,
|
Name: typeName,
|
||||||
Obj: obj,
|
Obj: obj,
|
||||||
},
|
},
|
||||||
Type: typ.typeExpr,
|
Type: typ,
|
||||||
}
|
}
|
||||||
obj.Decl = typeSpec
|
obj.Decl = typeSpec
|
||||||
gen.Specs = append(gen.Specs, typeSpec)
|
gen.Specs = append(gen.Specs, typeSpec)
|
||||||
}
|
}
|
||||||
p.generated.Decls = append(p.generated.Decls, gen)
|
info.Decls = append(info.Decls, gen)
|
||||||
}
|
}
|
||||||
|
|
||||||
// findMissingCGoNames traverses the AST and finds all C.something names. Only
|
// findMissingCGoNames traverses the AST and finds all C.something names. Only
|
||||||
// these symbols are extracted from the parsed C AST and converted to the Go
|
// these symbols are extracted from the parsed C AST and converted to the Go
|
||||||
// equivalent.
|
// equivalent.
|
||||||
func (p *cgoPackage) findMissingCGoNames(cursor *astutil.Cursor) bool {
|
func (info *fileInfo) findMissingCGoNames(cursor *astutil.Cursor) bool {
|
||||||
switch node := cursor.Node().(type) {
|
switch node := cursor.Node().(type) {
|
||||||
case *ast.SelectorExpr:
|
case *ast.SelectorExpr:
|
||||||
x, ok := node.X.(*ast.Ident)
|
x, ok := node.X.(*ast.Ident)
|
||||||
@@ -586,10 +471,10 @@ func (p *cgoPackage) findMissingCGoNames(cursor *astutil.Cursor) bool {
|
|||||||
}
|
}
|
||||||
if x.Name == "C" {
|
if x.Name == "C" {
|
||||||
name := node.Sel.Name
|
name := node.Sel.Name
|
||||||
if _, ok := builtinAliases[name]; ok {
|
if _, ok := cgoBuiltinAliases[name]; ok {
|
||||||
name = "_Cgo_" + name
|
name = "_Cgo_" + name
|
||||||
}
|
}
|
||||||
p.missingSymbols[name] = struct{}{}
|
info.missingSymbols[name] = struct{}{}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
@@ -599,7 +484,7 @@ func (p *cgoPackage) findMissingCGoNames(cursor *astutil.Cursor) bool {
|
|||||||
// expressions. Such expressions are impossible to write in Go (a dot cannot be
|
// expressions. Such expressions are impossible to write in Go (a dot cannot be
|
||||||
// used in the middle of a name) so in practice all C identifiers live in a
|
// used in the middle of a name) so in practice all C identifiers live in a
|
||||||
// separate namespace (no _Cgo_ hacks like in gc).
|
// separate namespace (no _Cgo_ hacks like in gc).
|
||||||
func (p *cgoPackage) walker(cursor *astutil.Cursor) bool {
|
func (info *fileInfo) walker(cursor *astutil.Cursor) bool {
|
||||||
switch node := cursor.Node().(type) {
|
switch node := cursor.Node().(type) {
|
||||||
case *ast.CallExpr:
|
case *ast.CallExpr:
|
||||||
fun, ok := node.Fun.(*ast.SelectorExpr)
|
fun, ok := node.Fun.(*ast.SelectorExpr)
|
||||||
@@ -610,7 +495,7 @@ func (p *cgoPackage) walker(cursor *astutil.Cursor) bool {
|
|||||||
if !ok {
|
if !ok {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
if _, ok := p.functions[fun.Sel.Name]; ok && x.Name == "C" {
|
if _, ok := info.functions[fun.Sel.Name]; ok && x.Name == "C" {
|
||||||
node.Fun = &ast.Ident{
|
node.Fun = &ast.Ident{
|
||||||
NamePos: x.NamePos,
|
NamePos: x.NamePos,
|
||||||
Name: "C." + fun.Sel.Name,
|
Name: "C." + fun.Sel.Name,
|
||||||
@@ -623,7 +508,7 @@ func (p *cgoPackage) walker(cursor *astutil.Cursor) bool {
|
|||||||
}
|
}
|
||||||
if x.Name == "C" {
|
if x.Name == "C" {
|
||||||
name := "C." + node.Sel.Name
|
name := "C." + node.Sel.Name
|
||||||
if _, ok := p.functions[node.Sel.Name]; ok {
|
if _, ok := info.functions[node.Sel.Name]; ok {
|
||||||
name += "$funcaddr"
|
name += "$funcaddr"
|
||||||
}
|
}
|
||||||
cursor.Replace(&ast.Ident{
|
cursor.Replace(&ast.Ident{
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package cgo
|
package loader
|
||||||
|
|
||||||
// This file parses a fragment of C with libclang and stores the result for AST
|
// This file parses a fragment of C with libclang and stores the result for AST
|
||||||
// modification. It does not touch the AST itself.
|
// modification. It does not touch the AST itself.
|
||||||
@@ -47,7 +47,6 @@ CXType tinygo_clang_getCursorResultType(GoCXCursor c);
|
|||||||
int tinygo_clang_Cursor_getNumArguments(GoCXCursor c);
|
int tinygo_clang_Cursor_getNumArguments(GoCXCursor c);
|
||||||
GoCXCursor tinygo_clang_Cursor_getArgument(GoCXCursor c, unsigned i);
|
GoCXCursor tinygo_clang_Cursor_getArgument(GoCXCursor c, unsigned i);
|
||||||
CXSourceLocation tinygo_clang_getCursorLocation(GoCXCursor c);
|
CXSourceLocation tinygo_clang_getCursorLocation(GoCXCursor c);
|
||||||
CXSourceRange tinygo_clang_getCursorExtent(GoCXCursor c);
|
|
||||||
CXTranslationUnit tinygo_clang_Cursor_getTranslationUnit(GoCXCursor c);
|
CXTranslationUnit tinygo_clang_Cursor_getTranslationUnit(GoCXCursor c);
|
||||||
|
|
||||||
int tinygo_clang_globals_visitor(GoCXCursor c, GoCXCursor parent, CXClientData client_data);
|
int tinygo_clang_globals_visitor(GoCXCursor c, GoCXCursor parent, CXClientData client_data);
|
||||||
@@ -55,8 +54,8 @@ int tinygo_clang_struct_visitor(GoCXCursor c, GoCXCursor parent, CXClientData cl
|
|||||||
*/
|
*/
|
||||||
import "C"
|
import "C"
|
||||||
|
|
||||||
// storedRefs stores references to types, used for clang_visitChildren.
|
// refMap stores references to types, used for clang_visitChildren.
|
||||||
var storedRefs refMap
|
var refMap RefMap
|
||||||
|
|
||||||
var diagnosticSeverity = [...]string{
|
var diagnosticSeverity = [...]string{
|
||||||
C.CXDiagnostic_Ignored: "ignored",
|
C.CXDiagnostic_Ignored: "ignored",
|
||||||
@@ -66,7 +65,7 @@ var diagnosticSeverity = [...]string{
|
|||||||
C.CXDiagnostic_Fatal: "fatal",
|
C.CXDiagnostic_Fatal: "fatal",
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *cgoPackage) parseFragment(fragment string, cflags []string, posFilename string, posLine int) {
|
func (info *fileInfo) parseFragment(fragment string, cflags []string, posFilename string, posLine int) []error {
|
||||||
index := C.clang_createIndex(0, 0)
|
index := C.clang_createIndex(0, 0)
|
||||||
defer C.clang_disposeIndex(index)
|
defer C.clang_disposeIndex(index)
|
||||||
|
|
||||||
@@ -102,7 +101,7 @@ func (p *cgoPackage) parseFragment(fragment string, cflags []string, posFilename
|
|||||||
filenameC,
|
filenameC,
|
||||||
(**C.char)(cmdargsC), C.int(len(cflags)), // command line args
|
(**C.char)(cmdargsC), C.int(len(cflags)), // command line args
|
||||||
&unsavedFile, 1, // unsaved files
|
&unsavedFile, 1, // unsaved files
|
||||||
C.CXTranslationUnit_DetailedPreprocessingRecord,
|
C.CXTranslationUnit_None,
|
||||||
&unit)
|
&unit)
|
||||||
if errCode != 0 {
|
if errCode != 0 {
|
||||||
panic("loader: failed to parse source with libclang")
|
panic("loader: failed to parse source with libclang")
|
||||||
@@ -110,6 +109,7 @@ func (p *cgoPackage) parseFragment(fragment string, cflags []string, posFilename
|
|||||||
defer C.clang_disposeTranslationUnit(unit)
|
defer C.clang_disposeTranslationUnit(unit)
|
||||||
|
|
||||||
if numDiagnostics := int(C.clang_getNumDiagnostics(unit)); numDiagnostics != 0 {
|
if numDiagnostics := int(C.clang_getNumDiagnostics(unit)); numDiagnostics != 0 {
|
||||||
|
errs := []error{}
|
||||||
addDiagnostic := func(diagnostic C.CXDiagnostic) {
|
addDiagnostic := func(diagnostic C.CXDiagnostic) {
|
||||||
spelling := getString(C.clang_getDiagnosticSpelling(diagnostic))
|
spelling := getString(C.clang_getDiagnosticSpelling(diagnostic))
|
||||||
severity := diagnosticSeverity[C.clang_getDiagnosticSeverity(diagnostic)]
|
severity := diagnosticSeverity[C.clang_getDiagnosticSeverity(diagnostic)]
|
||||||
@@ -121,12 +121,12 @@ func (p *cgoPackage) parseFragment(fragment string, cflags []string, posFilename
|
|||||||
filename := getString(libclangFilename)
|
filename := getString(libclangFilename)
|
||||||
if filepath.IsAbs(filename) {
|
if filepath.IsAbs(filename) {
|
||||||
// Relative paths for readability, like other Go parser errors.
|
// Relative paths for readability, like other Go parser errors.
|
||||||
relpath, err := filepath.Rel(p.dir, filename)
|
relpath, err := filepath.Rel(info.Program.Dir, filename)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
filename = relpath
|
filename = relpath
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
p.errors = append(p.errors, &scanner.Error{
|
errs = append(errs, &scanner.Error{
|
||||||
Pos: token.Position{
|
Pos: token.Position{
|
||||||
Filename: filename,
|
Filename: filename,
|
||||||
Offset: 0, // not provided by clang_getPresumedLocation
|
Offset: 0, // not provided by clang_getPresumedLocation
|
||||||
@@ -146,24 +146,26 @@ func (p *cgoPackage) parseFragment(fragment string, cflags []string, posFilename
|
|||||||
addDiagnostic(C.clang_getDiagnosticInSet(diagnostics, C.uint(j)))
|
addDiagnostic(C.clang_getDiagnosticInSet(diagnostics, C.uint(j)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return
|
return errs
|
||||||
}
|
}
|
||||||
|
|
||||||
ref := storedRefs.Put(p)
|
ref := refMap.Put(info)
|
||||||
defer storedRefs.Remove(ref)
|
defer refMap.Remove(ref)
|
||||||
cursor := C.tinygo_clang_getTranslationUnitCursor(unit)
|
cursor := C.tinygo_clang_getTranslationUnitCursor(unit)
|
||||||
C.tinygo_clang_visitChildren(cursor, C.CXCursorVisitor(C.tinygo_clang_globals_visitor), C.CXClientData(ref))
|
C.tinygo_clang_visitChildren(cursor, C.CXCursorVisitor(C.tinygo_clang_globals_visitor), C.CXClientData(ref))
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
//export tinygo_clang_globals_visitor
|
//export tinygo_clang_globals_visitor
|
||||||
func tinygo_clang_globals_visitor(c, parent C.GoCXCursor, client_data C.CXClientData) C.int {
|
func tinygo_clang_globals_visitor(c, parent C.GoCXCursor, client_data C.CXClientData) C.int {
|
||||||
p := storedRefs.Get(unsafe.Pointer(client_data)).(*cgoPackage)
|
info := refMap.Get(unsafe.Pointer(client_data)).(*fileInfo)
|
||||||
kind := C.tinygo_clang_getCursorKind(c)
|
kind := C.tinygo_clang_getCursorKind(c)
|
||||||
pos := p.getCursorPosition(c)
|
pos := info.getCursorPosition(c)
|
||||||
switch kind {
|
switch kind {
|
||||||
case C.CXCursor_FunctionDecl:
|
case C.CXCursor_FunctionDecl:
|
||||||
name := getString(C.tinygo_clang_getCursorSpelling(c))
|
name := getString(C.tinygo_clang_getCursorSpelling(c))
|
||||||
if _, required := p.missingSymbols[name]; !required {
|
if _, required := info.missingSymbols[name]; !required {
|
||||||
return C.CXChildVisit_Continue
|
return C.CXChildVisit_Continue
|
||||||
}
|
}
|
||||||
cursorType := C.tinygo_clang_getCursorType(c)
|
cursorType := C.tinygo_clang_getCursorType(c)
|
||||||
@@ -171,10 +173,8 @@ func tinygo_clang_globals_visitor(c, parent C.GoCXCursor, client_data C.CXClient
|
|||||||
return C.CXChildVisit_Continue // not supported
|
return C.CXChildVisit_Continue // not supported
|
||||||
}
|
}
|
||||||
numArgs := int(C.tinygo_clang_Cursor_getNumArguments(c))
|
numArgs := int(C.tinygo_clang_Cursor_getNumArguments(c))
|
||||||
fn := &functionInfo{
|
fn := &functionInfo{}
|
||||||
pos: pos,
|
info.functions[name] = fn
|
||||||
}
|
|
||||||
p.functions[name] = fn
|
|
||||||
for i := 0; i < numArgs; i++ {
|
for i := 0; i < numArgs; i++ {
|
||||||
arg := C.tinygo_clang_Cursor_getArgument(c, C.uint(i))
|
arg := C.tinygo_clang_Cursor_getArgument(c, C.uint(i))
|
||||||
argName := getString(C.tinygo_clang_getCursorSpelling(arg))
|
argName := getString(C.tinygo_clang_getCursorSpelling(arg))
|
||||||
@@ -184,7 +184,7 @@ func tinygo_clang_globals_visitor(c, parent C.GoCXCursor, client_data C.CXClient
|
|||||||
}
|
}
|
||||||
fn.args = append(fn.args, paramInfo{
|
fn.args = append(fn.args, paramInfo{
|
||||||
name: argName,
|
name: argName,
|
||||||
typeExpr: p.makeASTType(argType, pos),
|
typeExpr: info.makeASTType(argType, pos),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
resultType := C.tinygo_clang_getCursorResultType(c)
|
resultType := C.tinygo_clang_getCursorResultType(c)
|
||||||
@@ -192,7 +192,7 @@ func tinygo_clang_globals_visitor(c, parent C.GoCXCursor, client_data C.CXClient
|
|||||||
fn.results = &ast.FieldList{
|
fn.results = &ast.FieldList{
|
||||||
List: []*ast.Field{
|
List: []*ast.Field{
|
||||||
&ast.Field{
|
&ast.Field{
|
||||||
Type: p.makeASTType(resultType, pos),
|
Type: info.makeASTType(resultType, pos),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@@ -200,106 +200,25 @@ func tinygo_clang_globals_visitor(c, parent C.GoCXCursor, client_data C.CXClient
|
|||||||
case C.CXCursor_StructDecl:
|
case C.CXCursor_StructDecl:
|
||||||
typ := C.tinygo_clang_getCursorType(c)
|
typ := C.tinygo_clang_getCursorType(c)
|
||||||
name := getString(C.tinygo_clang_getCursorSpelling(c))
|
name := getString(C.tinygo_clang_getCursorSpelling(c))
|
||||||
if _, required := p.missingSymbols["struct_"+name]; !required {
|
if _, required := info.missingSymbols["struct_"+name]; !required {
|
||||||
return C.CXChildVisit_Continue
|
return C.CXChildVisit_Continue
|
||||||
}
|
}
|
||||||
p.makeASTType(typ, pos)
|
info.makeASTType(typ, pos)
|
||||||
case C.CXCursor_TypedefDecl:
|
case C.CXCursor_TypedefDecl:
|
||||||
typedefType := C.tinygo_clang_getCursorType(c)
|
typedefType := C.tinygo_clang_getCursorType(c)
|
||||||
name := getString(C.clang_getTypedefName(typedefType))
|
name := getString(C.clang_getTypedefName(typedefType))
|
||||||
if _, required := p.missingSymbols[name]; !required {
|
if _, required := info.missingSymbols[name]; !required {
|
||||||
return C.CXChildVisit_Continue
|
return C.CXChildVisit_Continue
|
||||||
}
|
}
|
||||||
p.makeASTType(typedefType, pos)
|
info.makeASTType(typedefType, pos)
|
||||||
case C.CXCursor_VarDecl:
|
case C.CXCursor_VarDecl:
|
||||||
name := getString(C.tinygo_clang_getCursorSpelling(c))
|
name := getString(C.tinygo_clang_getCursorSpelling(c))
|
||||||
if _, required := p.missingSymbols[name]; !required {
|
if _, required := info.missingSymbols[name]; !required {
|
||||||
return C.CXChildVisit_Continue
|
return C.CXChildVisit_Continue
|
||||||
}
|
}
|
||||||
cursorType := C.tinygo_clang_getCursorType(c)
|
cursorType := C.tinygo_clang_getCursorType(c)
|
||||||
p.globals[name] = globalInfo{
|
info.globals[name] = &globalInfo{
|
||||||
typeExpr: p.makeASTType(cursorType, pos),
|
typeExpr: info.makeASTType(cursorType, pos),
|
||||||
pos: pos,
|
|
||||||
}
|
|
||||||
case C.CXCursor_MacroDefinition:
|
|
||||||
name := getString(C.tinygo_clang_getCursorSpelling(c))
|
|
||||||
if _, required := p.missingSymbols[name]; !required {
|
|
||||||
return C.CXChildVisit_Continue
|
|
||||||
}
|
|
||||||
sourceRange := C.tinygo_clang_getCursorExtent(c)
|
|
||||||
start := C.clang_getRangeStart(sourceRange)
|
|
||||||
end := C.clang_getRangeEnd(sourceRange)
|
|
||||||
var file, endFile C.CXFile
|
|
||||||
var startOffset, endOffset C.unsigned
|
|
||||||
C.clang_getExpansionLocation(start, &file, nil, nil, &startOffset)
|
|
||||||
if file == nil {
|
|
||||||
panic("could not find file where macro is defined")
|
|
||||||
}
|
|
||||||
C.clang_getExpansionLocation(end, &endFile, nil, nil, &endOffset)
|
|
||||||
if file != endFile {
|
|
||||||
panic("expected start and end location of a #define to be in the same file")
|
|
||||||
}
|
|
||||||
if startOffset > endOffset {
|
|
||||||
panic("startOffset > endOffset")
|
|
||||||
}
|
|
||||||
|
|
||||||
// read file contents and extract the relevant byte range
|
|
||||||
tu := C.tinygo_clang_Cursor_getTranslationUnit(c)
|
|
||||||
var size C.size_t
|
|
||||||
sourcePtr := C.clang_getFileContents(tu, file, &size)
|
|
||||||
if endOffset >= C.uint(size) {
|
|
||||||
panic("endOffset lies after end of file")
|
|
||||||
}
|
|
||||||
source := string(((*[1 << 28]byte)(unsafe.Pointer(sourcePtr)))[startOffset:endOffset:endOffset])
|
|
||||||
if !strings.HasPrefix(source, name) {
|
|
||||||
panic(fmt.Sprintf("expected #define value to start with %#v, got %#v", name, source))
|
|
||||||
}
|
|
||||||
value := strings.TrimSpace(source[len(name):])
|
|
||||||
for len(value) != 0 && value[0] == '(' && value[len(value)-1] == ')' {
|
|
||||||
value = strings.TrimSpace(value[1 : len(value)-1])
|
|
||||||
}
|
|
||||||
if len(value) == 0 {
|
|
||||||
// Pretend it doesn't exist at all.
|
|
||||||
return C.CXChildVisit_Continue
|
|
||||||
}
|
|
||||||
// For information about integer literals:
|
|
||||||
// https://en.cppreference.com/w/cpp/language/integer_literal
|
|
||||||
if value[0] == '"' {
|
|
||||||
// string constant
|
|
||||||
p.constants[name] = constantInfo{&ast.BasicLit{pos, token.STRING, value}, pos}
|
|
||||||
return C.CXChildVisit_Continue
|
|
||||||
}
|
|
||||||
if value[0] == '\'' {
|
|
||||||
// char constant
|
|
||||||
p.constants[name] = constantInfo{&ast.BasicLit{pos, token.CHAR, value}, pos}
|
|
||||||
return C.CXChildVisit_Continue
|
|
||||||
}
|
|
||||||
// assume it's a number (int or float)
|
|
||||||
value = strings.Replace(value, "'", "", -1) // remove ' chars
|
|
||||||
value = strings.TrimRight(value, "lu") // remove llu suffixes etc.
|
|
||||||
// find the first non-number
|
|
||||||
nonnum := byte(0)
|
|
||||||
for i := 0; i < len(value); i++ {
|
|
||||||
if value[i] < '0' || value[i] > '9' {
|
|
||||||
nonnum = value[i]
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// determine number type based on the first non-number
|
|
||||||
switch nonnum {
|
|
||||||
case 0:
|
|
||||||
// no non-number found, must be an integer
|
|
||||||
p.constants[name] = constantInfo{&ast.BasicLit{pos, token.INT, value}, pos}
|
|
||||||
case 'x', 'X':
|
|
||||||
// hex integer constant
|
|
||||||
// TODO: may also be a floating point number per C++17.
|
|
||||||
p.constants[name] = constantInfo{&ast.BasicLit{pos, token.INT, value}, pos}
|
|
||||||
case '.', 'e':
|
|
||||||
// float constant
|
|
||||||
value = strings.TrimRight(value, "fFlL")
|
|
||||||
p.constants[name] = constantInfo{&ast.BasicLit{pos, token.FLOAT, value}, pos}
|
|
||||||
default:
|
|
||||||
// unknown type, ignore
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return C.CXChildVisit_Continue
|
return C.CXChildVisit_Continue
|
||||||
@@ -315,7 +234,7 @@ func getString(clangString C.CXString) (s string) {
|
|||||||
// getCursorPosition returns a usable token.Pos from a libclang cursor. If the
|
// getCursorPosition returns a usable token.Pos from a libclang cursor. If the
|
||||||
// file for this cursor has not been seen before, it is read from libclang
|
// file for this cursor has not been seen before, it is read from libclang
|
||||||
// (which already has the file in memory) and added to the token.FileSet.
|
// (which already has the file in memory) and added to the token.FileSet.
|
||||||
func (p *cgoPackage) getCursorPosition(cursor C.GoCXCursor) token.Pos {
|
func (info *fileInfo) getCursorPosition(cursor C.GoCXCursor) token.Pos {
|
||||||
location := C.tinygo_clang_getCursorLocation(cursor)
|
location := C.tinygo_clang_getCursorLocation(cursor)
|
||||||
var file C.CXFile
|
var file C.CXFile
|
||||||
var line C.unsigned
|
var line C.unsigned
|
||||||
@@ -327,7 +246,7 @@ func (p *cgoPackage) getCursorPosition(cursor C.GoCXCursor) token.Pos {
|
|||||||
return token.NoPos
|
return token.NoPos
|
||||||
}
|
}
|
||||||
filename := getString(C.clang_getFileName(file))
|
filename := getString(C.clang_getFileName(file))
|
||||||
if _, ok := p.tokenFiles[filename]; !ok {
|
if _, ok := info.tokenFiles[filename]; !ok {
|
||||||
// File has not been seen before in this package, add line information
|
// File has not been seen before in this package, add line information
|
||||||
// now by reading the file from libclang.
|
// now by reading the file from libclang.
|
||||||
tu := C.tinygo_clang_Cursor_getTranslationUnit(cursor)
|
tu := C.tinygo_clang_Cursor_getTranslationUnit(cursor)
|
||||||
@@ -340,16 +259,16 @@ func (p *cgoPackage) getCursorPosition(cursor C.GoCXCursor) token.Pos {
|
|||||||
lines = append(lines, i+1)
|
lines = append(lines, i+1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
f := p.fset.AddFile(filename, -1, int(size))
|
f := info.fset.AddFile(filename, -1, int(size))
|
||||||
f.SetLines(lines)
|
f.SetLines(lines)
|
||||||
p.tokenFiles[filename] = f
|
info.tokenFiles[filename] = f
|
||||||
}
|
}
|
||||||
return p.tokenFiles[filename].Pos(int(offset))
|
return info.tokenFiles[filename].Pos(int(offset))
|
||||||
}
|
}
|
||||||
|
|
||||||
// makeASTType return the ast.Expr for the given libclang type. In other words,
|
// makeASTType return the ast.Expr for the given libclang type. In other words,
|
||||||
// it converts a libclang type to a type in the Go AST.
|
// it converts a libclang type to a type in the Go AST.
|
||||||
func (p *cgoPackage) makeASTType(typ C.CXType, pos token.Pos) ast.Expr {
|
func (info *fileInfo) makeASTType(typ C.CXType, pos token.Pos) ast.Expr {
|
||||||
var typeName string
|
var typeName string
|
||||||
switch typ.kind {
|
switch typ.kind {
|
||||||
case C.CXType_Char_S, C.CXType_Char_U:
|
case C.CXType_Char_S, C.CXType_Char_U:
|
||||||
@@ -410,7 +329,7 @@ func (p *cgoPackage) makeASTType(typ C.CXType, pos token.Pos) ast.Expr {
|
|||||||
}
|
}
|
||||||
return &ast.StarExpr{
|
return &ast.StarExpr{
|
||||||
Star: pos,
|
Star: pos,
|
||||||
X: p.makeASTType(pointeeType, pos),
|
X: info.makeASTType(pointeeType, pos),
|
||||||
}
|
}
|
||||||
case C.CXType_ConstantArray:
|
case C.CXType_ConstantArray:
|
||||||
return &ast.ArrayType{
|
return &ast.ArrayType{
|
||||||
@@ -420,7 +339,7 @@ func (p *cgoPackage) makeASTType(typ C.CXType, pos token.Pos) ast.Expr {
|
|||||||
Kind: token.INT,
|
Kind: token.INT,
|
||||||
Value: strconv.FormatInt(int64(C.clang_getArraySize(typ)), 10),
|
Value: strconv.FormatInt(int64(C.clang_getArraySize(typ)), 10),
|
||||||
},
|
},
|
||||||
Elt: p.makeASTType(C.clang_getElementType(typ), pos),
|
Elt: info.makeASTType(C.clang_getElementType(typ), pos),
|
||||||
}
|
}
|
||||||
case C.CXType_FunctionProto:
|
case C.CXType_FunctionProto:
|
||||||
// Be compatible with gc, which uses the *[0]byte type for function
|
// Be compatible with gc, which uses the *[0]byte type for function
|
||||||
@@ -441,11 +360,11 @@ func (p *cgoPackage) makeASTType(typ C.CXType, pos token.Pos) ast.Expr {
|
|||||||
}
|
}
|
||||||
case C.CXType_Typedef:
|
case C.CXType_Typedef:
|
||||||
name := getString(C.clang_getTypedefName(typ))
|
name := getString(C.clang_getTypedefName(typ))
|
||||||
if _, ok := p.typedefs[name]; !ok {
|
if _, ok := info.typedefs[name]; !ok {
|
||||||
p.typedefs[name] = nil // don't recurse
|
info.typedefs[name] = nil // don't recurse
|
||||||
c := C.tinygo_clang_getTypeDeclaration(typ)
|
c := C.tinygo_clang_getTypeDeclaration(typ)
|
||||||
underlyingType := C.tinygo_clang_getTypedefDeclUnderlyingType(c)
|
underlyingType := C.tinygo_clang_getTypedefDeclUnderlyingType(c)
|
||||||
expr := p.makeASTType(underlyingType, pos)
|
expr := info.makeASTType(underlyingType, pos)
|
||||||
if strings.HasPrefix(name, "_Cgo_") {
|
if strings.HasPrefix(name, "_Cgo_") {
|
||||||
expr := expr.(*ast.Ident)
|
expr := expr.(*ast.Ident)
|
||||||
typeSize := C.clang_Type_getSizeOf(underlyingType)
|
typeSize := C.clang_Type_getSizeOf(underlyingType)
|
||||||
@@ -487,9 +406,8 @@ func (p *cgoPackage) makeASTType(typ C.CXType, pos token.Pos) ast.Expr {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
p.typedefs[name] = &typedefInfo{
|
info.typedefs[name] = &typedefInfo{
|
||||||
typeExpr: expr,
|
typeExpr: expr,
|
||||||
pos: pos,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return &ast.Ident{
|
return &ast.Ident{
|
||||||
@@ -500,7 +418,7 @@ func (p *cgoPackage) makeASTType(typ C.CXType, pos token.Pos) ast.Expr {
|
|||||||
underlying := C.clang_Type_getNamedType(typ)
|
underlying := C.clang_Type_getNamedType(typ)
|
||||||
switch underlying.kind {
|
switch underlying.kind {
|
||||||
case C.CXType_Record:
|
case C.CXType_Record:
|
||||||
return p.makeASTType(underlying, pos)
|
return info.makeASTType(underlying, pos)
|
||||||
default:
|
default:
|
||||||
panic("unknown elaborated type")
|
panic("unknown elaborated type")
|
||||||
}
|
}
|
||||||
@@ -516,26 +434,23 @@ func (p *cgoPackage) makeASTType(typ C.CXType, pos token.Pos) ast.Expr {
|
|||||||
default:
|
default:
|
||||||
panic("unknown record declaration")
|
panic("unknown record declaration")
|
||||||
}
|
}
|
||||||
if _, ok := p.elaboratedTypes[cgoName]; !ok {
|
if _, ok := info.elaboratedTypes[cgoName]; !ok {
|
||||||
p.elaboratedTypes[cgoName] = nil // predeclare (to avoid endless recursion)
|
info.elaboratedTypes[cgoName] = nil // predeclare (to avoid endless recursion)
|
||||||
fieldList := &ast.FieldList{
|
fieldList := &ast.FieldList{
|
||||||
Opening: pos,
|
Opening: pos,
|
||||||
Closing: pos,
|
Closing: pos,
|
||||||
}
|
}
|
||||||
ref := storedRefs.Put(struct {
|
ref := refMap.Put(struct {
|
||||||
fieldList *ast.FieldList
|
fieldList *ast.FieldList
|
||||||
pkg *cgoPackage
|
info *fileInfo
|
||||||
}{fieldList, p})
|
}{fieldList, info})
|
||||||
defer storedRefs.Remove(ref)
|
defer refMap.Remove(ref)
|
||||||
C.tinygo_clang_visitChildren(cursor, C.CXCursorVisitor(C.tinygo_clang_struct_visitor), C.CXClientData(ref))
|
C.tinygo_clang_visitChildren(cursor, C.CXCursorVisitor(C.tinygo_clang_struct_visitor), C.CXClientData(ref))
|
||||||
switch C.tinygo_clang_getCursorKind(cursor) {
|
switch C.tinygo_clang_getCursorKind(cursor) {
|
||||||
case C.CXCursor_StructDecl:
|
case C.CXCursor_StructDecl:
|
||||||
p.elaboratedTypes[cgoName] = &elaboratedTypeInfo{
|
info.elaboratedTypes[cgoName] = &ast.StructType{
|
||||||
typeExpr: &ast.StructType{
|
Struct: pos,
|
||||||
Struct: pos,
|
Fields: fieldList,
|
||||||
Fields: fieldList,
|
|
||||||
},
|
|
||||||
pos: pos,
|
|
||||||
}
|
}
|
||||||
case C.CXCursor_UnionDecl:
|
case C.CXCursor_UnionDecl:
|
||||||
if len(fieldList.List) > 1 {
|
if len(fieldList.List) > 1 {
|
||||||
@@ -565,12 +480,9 @@ func (p *cgoPackage) makeASTType(typ C.CXType, pos token.Pos) ast.Expr {
|
|||||||
}
|
}
|
||||||
fieldList.List = append([]*ast.Field{unionMarker}, fieldList.List...)
|
fieldList.List = append([]*ast.Field{unionMarker}, fieldList.List...)
|
||||||
}
|
}
|
||||||
p.elaboratedTypes[cgoName] = &elaboratedTypeInfo{
|
info.elaboratedTypes[cgoName] = &ast.StructType{
|
||||||
typeExpr: &ast.StructType{
|
Struct: pos,
|
||||||
Struct: pos,
|
Fields: fieldList,
|
||||||
Fields: fieldList,
|
|
||||||
},
|
|
||||||
pos: pos,
|
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
panic("unreachable")
|
panic("unreachable")
|
||||||
@@ -594,23 +506,23 @@ func (p *cgoPackage) makeASTType(typ C.CXType, pos token.Pos) ast.Expr {
|
|||||||
|
|
||||||
//export tinygo_clang_struct_visitor
|
//export tinygo_clang_struct_visitor
|
||||||
func tinygo_clang_struct_visitor(c, parent C.GoCXCursor, client_data C.CXClientData) C.int {
|
func tinygo_clang_struct_visitor(c, parent C.GoCXCursor, client_data C.CXClientData) C.int {
|
||||||
passed := storedRefs.Get(unsafe.Pointer(client_data)).(struct {
|
passed := refMap.Get(unsafe.Pointer(client_data)).(struct {
|
||||||
fieldList *ast.FieldList
|
fieldList *ast.FieldList
|
||||||
pkg *cgoPackage
|
info *fileInfo
|
||||||
})
|
})
|
||||||
fieldList := passed.fieldList
|
fieldList := passed.fieldList
|
||||||
p := passed.pkg
|
info := passed.info
|
||||||
if C.tinygo_clang_getCursorKind(c) != C.CXCursor_FieldDecl {
|
if C.tinygo_clang_getCursorKind(c) != C.CXCursor_FieldDecl {
|
||||||
panic("expected field inside cursor")
|
panic("expected field inside cursor")
|
||||||
}
|
}
|
||||||
name := getString(C.tinygo_clang_getCursorSpelling(c))
|
name := getString(C.tinygo_clang_getCursorSpelling(c))
|
||||||
typ := C.tinygo_clang_getCursorType(c)
|
typ := C.tinygo_clang_getCursorType(c)
|
||||||
field := &ast.Field{
|
field := &ast.Field{
|
||||||
Type: p.makeASTType(typ, p.getCursorPosition(c)),
|
Type: info.makeASTType(typ, info.getCursorPosition(c)),
|
||||||
}
|
}
|
||||||
field.Names = []*ast.Ident{
|
field.Names = []*ast.Ident{
|
||||||
&ast.Ident{
|
&ast.Ident{
|
||||||
NamePos: p.getCursorPosition(c),
|
NamePos: info.getCursorPosition(c),
|
||||||
Name: name,
|
Name: name,
|
||||||
Obj: &ast.Object{
|
Obj: &ast.Object{
|
||||||
Kind: ast.Var,
|
Kind: ast.Var,
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
// +build !byollvm
|
// +build !byollvm
|
||||||
|
|
||||||
package cgo
|
package loader
|
||||||
|
|
||||||
/*
|
/*
|
||||||
#cgo linux CFLAGS: -I/usr/lib/llvm-8/include
|
#cgo linux CFLAGS: -I/usr/lib/llvm-8/include
|
||||||
@@ -49,10 +49,6 @@ CXSourceLocation tinygo_clang_getCursorLocation(CXCursor c) {
|
|||||||
return clang_getCursorLocation(c);
|
return clang_getCursorLocation(c);
|
||||||
}
|
}
|
||||||
|
|
||||||
CXSourceRange tinygo_clang_getCursorExtent(CXCursor c) {
|
|
||||||
return clang_getCursorExtent(c);
|
|
||||||
}
|
|
||||||
|
|
||||||
CXTranslationUnit tinygo_clang_Cursor_getTranslationUnit(CXCursor c) {
|
CXTranslationUnit tinygo_clang_Cursor_getTranslationUnit(CXCursor c) {
|
||||||
return clang_Cursor_getTranslationUnit(c);
|
return clang_Cursor_getTranslationUnit(c);
|
||||||
}
|
}
|
||||||
+19
-19
@@ -10,8 +10,6 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"sort"
|
"sort"
|
||||||
|
|
||||||
"github.com/tinygo-org/tinygo/cgo"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Program holds all packages and some metadata about the program as a whole.
|
// Program holds all packages and some metadata about the program as a whole.
|
||||||
@@ -32,10 +30,11 @@ type Program struct {
|
|||||||
type Package struct {
|
type Package struct {
|
||||||
*Program
|
*Program
|
||||||
*build.Package
|
*build.Package
|
||||||
Imports map[string]*Package
|
Imports map[string]*Package
|
||||||
Importing bool
|
Importing bool
|
||||||
Files []*ast.File
|
Files []*ast.File
|
||||||
Pkg *types.Package
|
tokenFiles map[string]*token.File
|
||||||
|
Pkg *types.Package
|
||||||
types.Info
|
types.Info
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -108,6 +107,7 @@ func (p *Program) newPackage(pkg *build.Package) *Package {
|
|||||||
Scopes: make(map[ast.Node]*types.Scope),
|
Scopes: make(map[ast.Node]*types.Scope),
|
||||||
Selections: make(map[*ast.SelectorExpr]*types.Selection),
|
Selections: make(map[*ast.SelectorExpr]*types.Selection),
|
||||||
},
|
},
|
||||||
|
tokenFiles: map[string]*token.File{},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -295,17 +295,8 @@ func (p *Package) parseFiles() ([]*ast.File, error) {
|
|||||||
}
|
}
|
||||||
files = append(files, f)
|
files = append(files, f)
|
||||||
}
|
}
|
||||||
for _, file := range p.CgoFiles {
|
clangIncludes := ""
|
||||||
path := filepath.Join(p.Package.Dir, file)
|
|
||||||
f, err := p.parseFile(path, parser.ParseComments)
|
|
||||||
if err != nil {
|
|
||||||
fileErrs = append(fileErrs, err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
files = append(files, f)
|
|
||||||
}
|
|
||||||
if len(p.CgoFiles) != 0 {
|
if len(p.CgoFiles) != 0 {
|
||||||
clangIncludes := ""
|
|
||||||
if _, err := os.Stat(filepath.Join(p.TINYGOROOT, "llvm", "tools", "clang", "lib", "Headers")); !os.IsNotExist(err) {
|
if _, err := os.Stat(filepath.Join(p.TINYGOROOT, "llvm", "tools", "clang", "lib", "Headers")); !os.IsNotExist(err) {
|
||||||
// Running from the source directory.
|
// Running from the source directory.
|
||||||
clangIncludes = filepath.Join(p.TINYGOROOT, "llvm", "tools", "clang", "lib", "Headers")
|
clangIncludes = filepath.Join(p.TINYGOROOT, "llvm", "tools", "clang", "lib", "Headers")
|
||||||
@@ -313,11 +304,20 @@ func (p *Package) parseFiles() ([]*ast.File, error) {
|
|||||||
// Running from the installation directory.
|
// Running from the installation directory.
|
||||||
clangIncludes = filepath.Join(p.TINYGOROOT, "lib", "clang", "include")
|
clangIncludes = filepath.Join(p.TINYGOROOT, "lib", "clang", "include")
|
||||||
}
|
}
|
||||||
generated, errs := cgo.Process(files, p.Program.Dir, p.fset, append(p.CFlags, "-I"+p.Package.Dir, "-I"+clangIncludes))
|
}
|
||||||
|
for _, file := range p.CgoFiles {
|
||||||
|
path := filepath.Join(p.Package.Dir, file)
|
||||||
|
f, err := p.parseFile(path, parser.ParseComments)
|
||||||
|
if err != nil {
|
||||||
|
fileErrs = append(fileErrs, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
errs := p.processCgo(path, f, append(p.CFlags, "-I"+p.Package.Dir, "-I"+clangIncludes))
|
||||||
if errs != nil {
|
if errs != nil {
|
||||||
fileErrs = append(fileErrs, errs...)
|
fileErrs = append(fileErrs, errs...)
|
||||||
|
continue
|
||||||
}
|
}
|
||||||
files = append(files, generated)
|
files = append(files, f)
|
||||||
}
|
}
|
||||||
if len(fileErrs) != 0 {
|
if len(fileErrs) != 0 {
|
||||||
return nil, Errors{p, fileErrs}
|
return nil, Errors{p, fileErrs}
|
||||||
@@ -346,7 +346,7 @@ func (p *Package) importRecursively() error {
|
|||||||
p.Importing = true
|
p.Importing = true
|
||||||
for _, to := range p.Package.Imports {
|
for _, to := range p.Package.Imports {
|
||||||
if to == "C" {
|
if to == "C" {
|
||||||
// Do CGo processing in a later stage.
|
// Do Cgo processing in a later stage.
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if _, ok := p.Imports[to]; ok {
|
if _, ok := p.Imports[to]; ok {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
package cgo
|
package loader
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"sync"
|
"sync"
|
||||||
@@ -8,17 +8,17 @@ import (
|
|||||||
// #include <stdlib.h>
|
// #include <stdlib.h>
|
||||||
import "C"
|
import "C"
|
||||||
|
|
||||||
// refMap is a convenient way to store opaque references that can be passed to
|
// RefMap is a convenient way to store opaque references that can be passed to
|
||||||
// C. It is useful if an API uses function pointers and you cannot pass a Go
|
// C. It is useful if an API uses function pointers and you cannot pass a Go
|
||||||
// pointer but only a C pointer.
|
// pointer but only a C pointer.
|
||||||
type refMap struct {
|
type RefMap struct {
|
||||||
refs map[unsafe.Pointer]interface{}
|
refs map[unsafe.Pointer]interface{}
|
||||||
lock sync.Mutex
|
lock sync.Mutex
|
||||||
}
|
}
|
||||||
|
|
||||||
// Put stores a value in the map. It can later be retrieved using Get. It must
|
// Put stores a value in the map. It can later be retrieved using Get. It must
|
||||||
// be removed using Remove to avoid memory leaks.
|
// be removed using Remove to avoid memory leaks.
|
||||||
func (m *refMap) Put(v interface{}) unsafe.Pointer {
|
func (m *RefMap) Put(v interface{}) unsafe.Pointer {
|
||||||
m.lock.Lock()
|
m.lock.Lock()
|
||||||
defer m.lock.Unlock()
|
defer m.lock.Unlock()
|
||||||
if m.refs == nil {
|
if m.refs == nil {
|
||||||
@@ -31,14 +31,14 @@ func (m *refMap) Put(v interface{}) unsafe.Pointer {
|
|||||||
|
|
||||||
// Get returns a stored value previously inserted with Put. Use the same
|
// Get returns a stored value previously inserted with Put. Use the same
|
||||||
// reference as you got from Put.
|
// reference as you got from Put.
|
||||||
func (m *refMap) Get(ref unsafe.Pointer) interface{} {
|
func (m *RefMap) Get(ref unsafe.Pointer) interface{} {
|
||||||
m.lock.Lock()
|
m.lock.Lock()
|
||||||
defer m.lock.Unlock()
|
defer m.lock.Unlock()
|
||||||
return m.refs[ref]
|
return m.refs[ref]
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove deletes a single reference from the map.
|
// Remove deletes a single reference from the map.
|
||||||
func (m *refMap) Remove(ref unsafe.Pointer) {
|
func (m *RefMap) Remove(ref unsafe.Pointer) {
|
||||||
m.lock.Lock()
|
m.lock.Lock()
|
||||||
defer m.lock.Unlock()
|
defer m.lock.Unlock()
|
||||||
delete(m.refs, ref)
|
delete(m.refs, ref)
|
||||||
@@ -537,7 +537,7 @@ func handleCompilerError(err error) {
|
|||||||
func main() {
|
func main() {
|
||||||
outpath := flag.String("o", "", "output filename")
|
outpath := flag.String("o", "", "output filename")
|
||||||
opt := flag.String("opt", "z", "optimization level: 0, 1, 2, s, z")
|
opt := flag.String("opt", "z", "optimization level: 0, 1, 2, s, z")
|
||||||
gc := flag.String("gc", "", "garbage collector to use (none, leaking, conservative)")
|
gc := flag.String("gc", "", "garbage collector to use (none, dumb, marksweep)")
|
||||||
panicStrategy := flag.String("panic", "print", "panic strategy (abort, trap)")
|
panicStrategy := flag.String("panic", "print", "panic strategy (abort, trap)")
|
||||||
printIR := flag.Bool("printir", false, "print LLVM IR")
|
printIR := flag.Bool("printir", false, "print LLVM IR")
|
||||||
dumpSSA := flag.Bool("dumpssa", false, "dump internal Go SSA")
|
dumpSSA := flag.Bool("dumpssa", false, "dump internal Go SSA")
|
||||||
|
|||||||
@@ -58,9 +58,6 @@ func TestCompiler(t *testing.T) {
|
|||||||
|
|
||||||
t.Log("running tests for emulated cortex-m3...")
|
t.Log("running tests for emulated cortex-m3...")
|
||||||
for _, path := range matches {
|
for _, path := range matches {
|
||||||
if path == "testdata/reflect.go" {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
t.Run(path, func(t *testing.T) {
|
t.Run(path, func(t *testing.T) {
|
||||||
runTest(path, tmpdir, "qemu", t)
|
runTest(path, tmpdir, "qemu", t)
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,25 +0,0 @@
|
|||||||
// Example using the i2s hardware interface on the Adafruit Circuit Playground Express
|
|
||||||
// to read data from the onboard MEMS microphone.
|
|
||||||
//
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"machine"
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
machine.I2S0.Configure(machine.I2SConfig{
|
|
||||||
Mode: machine.I2SModePDM,
|
|
||||||
ClockSource: machine.I2SClockSourceExternal,
|
|
||||||
Stereo: true,
|
|
||||||
})
|
|
||||||
|
|
||||||
data := make([]uint32, 64)
|
|
||||||
|
|
||||||
for {
|
|
||||||
// get the next group of samples
|
|
||||||
machine.I2S0.Read(data)
|
|
||||||
|
|
||||||
println("data", data[0], data[1], data[2], data[4], "...")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -99,15 +99,3 @@ const (
|
|||||||
var (
|
var (
|
||||||
SPI0 = SPI{Bus: sam.SERCOM3_SPI}
|
SPI0 = SPI{Bus: sam.SERCOM3_SPI}
|
||||||
)
|
)
|
||||||
|
|
||||||
// I2S pins
|
|
||||||
const (
|
|
||||||
I2S_SCK_PIN = PA10
|
|
||||||
I2S_SD_PIN = PA08
|
|
||||||
I2S_WS_PIN = 0xff // no WS, instead uses SCK to sync
|
|
||||||
)
|
|
||||||
|
|
||||||
// I2S on the Circuit Playground Express.
|
|
||||||
var (
|
|
||||||
I2S0 = I2S{Bus: sam.I2S}
|
|
||||||
)
|
|
||||||
|
|||||||
@@ -73,15 +73,3 @@ const (
|
|||||||
var (
|
var (
|
||||||
SPI0 = SPI{Bus: sam.SERCOM4_SPI}
|
SPI0 = SPI{Bus: sam.SERCOM4_SPI}
|
||||||
)
|
)
|
||||||
|
|
||||||
// I2S pins
|
|
||||||
const (
|
|
||||||
I2S_SCK_PIN = PA10
|
|
||||||
I2S_SD_PIN = PA08
|
|
||||||
I2S_WS_PIN = 0xff // TODO: figure out what this is on ItsyBitsy M0.
|
|
||||||
)
|
|
||||||
|
|
||||||
// I2S on the ItsyBitsy M0.
|
|
||||||
var (
|
|
||||||
I2S0 = I2S{Bus: sam.I2S}
|
|
||||||
)
|
|
||||||
|
|||||||
@@ -1,54 +0,0 @@
|
|||||||
// +build sam
|
|
||||||
|
|
||||||
// This is the definition for I2S bus functions.
|
|
||||||
// Actual implementations if available for any given hardware
|
|
||||||
// are to be found in its the board definition.
|
|
||||||
//
|
|
||||||
// For more info about I2S, see: https://en.wikipedia.org/wiki/I%C2%B2S
|
|
||||||
//
|
|
||||||
|
|
||||||
package machine
|
|
||||||
|
|
||||||
type I2SMode uint8
|
|
||||||
type I2SStandard uint8
|
|
||||||
type I2SClockSource uint8
|
|
||||||
type I2SDataFormat uint8
|
|
||||||
|
|
||||||
const (
|
|
||||||
I2SModeMaster I2SMode = iota
|
|
||||||
I2SModeSlave
|
|
||||||
I2SModePDM
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
I2StandardPhilips I2SStandard = iota
|
|
||||||
I2SStandardMSB
|
|
||||||
I2SStandardLSB
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
I2SClockSourceInternal I2SClockSource = iota
|
|
||||||
I2SClockSourceExternal
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
I2SDataFormatDefault I2SDataFormat = 0
|
|
||||||
I2SDataFormat8bit = 8
|
|
||||||
I2SDataFormat16bit = 16
|
|
||||||
I2SDataFormat24bit = 24
|
|
||||||
I2SDataFormat32bit = 32
|
|
||||||
)
|
|
||||||
|
|
||||||
// All fields are optional and may not be required or used on a particular platform.
|
|
||||||
type I2SConfig struct {
|
|
||||||
SCK uint8
|
|
||||||
WS uint8
|
|
||||||
SD uint8
|
|
||||||
Mode I2SMode
|
|
||||||
Standard I2SStandard
|
|
||||||
ClockSource I2SClockSource
|
|
||||||
DataFormat I2SDataFormat
|
|
||||||
AudioFrequency uint32
|
|
||||||
MasterClockOutput bool
|
|
||||||
Stereo bool
|
|
||||||
}
|
|
||||||
@@ -10,15 +10,15 @@ import (
|
|||||||
func (p GPIO) Configure(config GPIOConfig) {
|
func (p GPIO) Configure(config GPIOConfig) {
|
||||||
if config.Mode == GPIO_OUTPUT { // set output bit
|
if config.Mode == GPIO_OUTPUT { // set output bit
|
||||||
if p.Pin < 8 {
|
if p.Pin < 8 {
|
||||||
avr.DDRD.SetBits(1 << p.Pin)
|
*avr.DDRD |= 1 << p.Pin
|
||||||
} else {
|
} else {
|
||||||
avr.DDRB.SetBits(1 << (p.Pin - 8))
|
*avr.DDRB |= 1 << (p.Pin - 8)
|
||||||
}
|
}
|
||||||
} else { // configure input: clear output bit
|
} else { // configure input: clear output bit
|
||||||
if p.Pin < 8 {
|
if p.Pin < 8 {
|
||||||
avr.DDRD.ClearBits(1 << p.Pin)
|
*avr.DDRD &^= 1 << p.Pin
|
||||||
} else {
|
} else {
|
||||||
avr.DDRB.ClearBits(1 << (p.Pin - 8))
|
*avr.DDRB &^= 1 << (p.Pin - 8)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -26,15 +26,15 @@ func (p GPIO) Configure(config GPIOConfig) {
|
|||||||
// Get returns the current value of a GPIO pin.
|
// Get returns the current value of a GPIO pin.
|
||||||
func (p GPIO) Get() bool {
|
func (p GPIO) Get() bool {
|
||||||
if p.Pin < 8 {
|
if p.Pin < 8 {
|
||||||
val := avr.PIND.Get() & (1 << p.Pin)
|
val := *avr.PIND & (1 << p.Pin)
|
||||||
return (val > 0)
|
return (val > 0)
|
||||||
} else {
|
} else {
|
||||||
val := avr.PINB.Get() & (1 << (p.Pin - 8))
|
val := *avr.PINB & (1 << (p.Pin - 8))
|
||||||
return (val > 0)
|
return (val > 0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p GPIO) getPortMask() (*avr.Register8, uint8) {
|
func (p GPIO) getPortMask() (*avr.RegValue, uint8) {
|
||||||
if p.Pin < 8 {
|
if p.Pin < 8 {
|
||||||
return avr.PORTD, 1 << p.Pin
|
return avr.PORTD, 1 << p.Pin
|
||||||
} else {
|
} else {
|
||||||
@@ -45,64 +45,64 @@ func (p GPIO) getPortMask() (*avr.Register8, uint8) {
|
|||||||
// InitPWM initializes the registers needed for PWM.
|
// InitPWM initializes the registers needed for PWM.
|
||||||
func InitPWM() {
|
func InitPWM() {
|
||||||
// use waveform generation
|
// use waveform generation
|
||||||
avr.TCCR0A.SetBits(avr.TCCR0A_WGM00)
|
*avr.TCCR0A |= avr.TCCR0A_WGM00
|
||||||
|
|
||||||
// set timer 0 prescale factor to 64
|
// set timer 0 prescale factor to 64
|
||||||
avr.TCCR0B.SetBits(avr.TCCR0B_CS01 | avr.TCCR0B_CS00)
|
*avr.TCCR0B |= avr.TCCR0B_CS01 | avr.TCCR0B_CS00
|
||||||
|
|
||||||
// set timer 1 prescale factor to 64
|
// set timer 1 prescale factor to 64
|
||||||
avr.TCCR1B.SetBits(avr.TCCR1B_CS11)
|
*avr.TCCR1B |= avr.TCCR1B_CS11
|
||||||
|
|
||||||
// put timer 1 in 8-bit phase correct pwm mode
|
// put timer 1 in 8-bit phase correct pwm mode
|
||||||
avr.TCCR1A.SetBits(avr.TCCR1A_WGM10)
|
*avr.TCCR1A |= avr.TCCR1A_WGM10
|
||||||
|
|
||||||
// set timer 2 prescale factor to 64
|
// set timer 2 prescale factor to 64
|
||||||
avr.TCCR2B.SetBits(avr.TCCR2B_CS22)
|
*avr.TCCR2B |= avr.TCCR2B_CS22
|
||||||
|
|
||||||
// configure timer 2 for phase correct pwm (8-bit)
|
// configure timer 2 for phase correct pwm (8-bit)
|
||||||
avr.TCCR2A.SetBits(avr.TCCR2A_WGM20)
|
*avr.TCCR2A |= avr.TCCR2A_WGM20
|
||||||
}
|
}
|
||||||
|
|
||||||
// Configure configures a PWM pin for output.
|
// Configure configures a PWM pin for output.
|
||||||
func (pwm PWM) Configure() {
|
func (pwm PWM) Configure() {
|
||||||
if pwm.Pin < 8 {
|
if pwm.Pin < 8 {
|
||||||
avr.DDRD.SetBits(1 << pwm.Pin)
|
*avr.DDRD |= 1 << pwm.Pin
|
||||||
} else {
|
} else {
|
||||||
avr.DDRB.SetBits(1 << (pwm.Pin - 8))
|
*avr.DDRB |= 1 << (pwm.Pin - 8)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set turns on the duty cycle for a PWM pin using the provided value. On the AVR this is normally a
|
// Set turns on the duty cycle for a PWM pin using the provided value. On the AVR this is normally a
|
||||||
// 8-bit value ranging from 0 to 255.
|
// 8-bit value ranging from 0 to 255.
|
||||||
func (pwm PWM) Set(value uint16) {
|
func (pwm PWM) Set(value uint16) {
|
||||||
value8 := uint8(value >> 8)
|
value8 := value >> 8
|
||||||
switch pwm.Pin {
|
switch pwm.Pin {
|
||||||
case 3:
|
case 3:
|
||||||
// connect pwm to pin on timer 2, channel B
|
// connect pwm to pin on timer 2, channel B
|
||||||
avr.TCCR2A.SetBits(avr.TCCR2A_COM2B1)
|
*avr.TCCR2A |= avr.TCCR2A_COM2B1
|
||||||
avr.OCR2B.Set(value8) // set pwm duty
|
*avr.OCR2B = avr.RegValue(value8) // set pwm duty
|
||||||
case 5:
|
case 5:
|
||||||
// connect pwm to pin on timer 0, channel B
|
// connect pwm to pin on timer 0, channel B
|
||||||
avr.TCCR0A.SetBits(avr.TCCR0A_COM0B1)
|
*avr.TCCR0A |= avr.TCCR0A_COM0B1
|
||||||
avr.OCR0B.Set(value8) // set pwm duty
|
*avr.OCR0B = avr.RegValue(value8) // set pwm duty
|
||||||
case 6:
|
case 6:
|
||||||
// connect pwm to pin on timer 0, channel A
|
// connect pwm to pin on timer 0, channel A
|
||||||
avr.TCCR0A.SetBits(avr.TCCR0A_COM0A1)
|
*avr.TCCR0A |= avr.TCCR0A_COM0A1
|
||||||
avr.OCR0A.Set(value8) // set pwm duty
|
*avr.OCR0A = avr.RegValue(value8) // set pwm duty
|
||||||
case 9:
|
case 9:
|
||||||
// connect pwm to pin on timer 1, channel A
|
// connect pwm to pin on timer 1, channel A
|
||||||
avr.TCCR1A.SetBits(avr.TCCR1A_COM1A1)
|
*avr.TCCR1A |= avr.TCCR1A_COM1A1
|
||||||
// this is a 16-bit value, but we only currently allow the low order bits to be set
|
// this is a 16-bit value, but we only currently allow the low order bits to be set
|
||||||
avr.OCR1AL.Set(value8) // set pwm duty
|
*avr.OCR1AL = avr.RegValue(value8) // set pwm duty
|
||||||
case 10:
|
case 10:
|
||||||
// connect pwm to pin on timer 1, channel B
|
// connect pwm to pin on timer 1, channel B
|
||||||
avr.TCCR1A.SetBits(avr.TCCR1A_COM1B1)
|
*avr.TCCR1A |= avr.TCCR1A_COM1B1
|
||||||
// this is a 16-bit value, but we only currently allow the low order bits to be set
|
// this is a 16-bit value, but we only currently allow the low order bits to be set
|
||||||
avr.OCR1BL.Set(value8) // set pwm duty
|
*avr.OCR1BL = avr.RegValue(value8) // set pwm duty
|
||||||
case 11:
|
case 11:
|
||||||
// connect pwm to pin on timer 2, channel A
|
// connect pwm to pin on timer 2, channel A
|
||||||
avr.TCCR2A.SetBits(avr.TCCR2A_COM2A1)
|
*avr.TCCR2A |= avr.TCCR2A_COM2A1
|
||||||
avr.OCR2A.Set(value8) // set pwm duty
|
*avr.OCR2A = avr.RegValue(value8) // set pwm duty
|
||||||
default:
|
default:
|
||||||
panic("Invalid PWM pin")
|
panic("Invalid PWM pin")
|
||||||
}
|
}
|
||||||
@@ -121,19 +121,19 @@ func (i2c I2C) Configure(config I2CConfig) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Activate internal pullups for twi.
|
// Activate internal pullups for twi.
|
||||||
avr.PORTC.SetBits((avr.DIDR0_ADC4D | avr.DIDR0_ADC5D))
|
*avr.PORTC |= (avr.DIDR0_ADC4D | avr.DIDR0_ADC5D)
|
||||||
|
|
||||||
// Initialize twi prescaler and bit rate.
|
// Initialize twi prescaler and bit rate.
|
||||||
avr.TWSR.SetBits((avr.TWSR_TWPS0 | avr.TWSR_TWPS1))
|
*avr.TWSR |= (avr.TWSR_TWPS0 | avr.TWSR_TWPS1)
|
||||||
|
|
||||||
// twi bit rate formula from atmega128 manual pg. 204:
|
// twi bit rate formula from atmega128 manual pg. 204:
|
||||||
// SCL Frequency = CPU Clock Frequency / (16 + (2 * TWBR))
|
// SCL Frequency = CPU Clock Frequency / (16 + (2 * TWBR))
|
||||||
// NOTE: TWBR should be 10 or higher for master mode.
|
// NOTE: TWBR should be 10 or higher for master mode.
|
||||||
// It is 72 for a 16mhz board with 100kHz TWI
|
// It is 72 for a 16mhz board with 100kHz TWI
|
||||||
avr.TWBR.Set(uint8(((CPU_FREQUENCY / config.Frequency) - 16) / 2))
|
*avr.TWBR = avr.RegValue(((CPU_FREQUENCY / config.Frequency) - 16) / 2)
|
||||||
|
|
||||||
// Enable twi module.
|
// Enable twi module.
|
||||||
avr.TWCR.Set(avr.TWCR_TWEN)
|
*avr.TWCR = avr.TWCR_TWEN
|
||||||
}
|
}
|
||||||
|
|
||||||
// Tx does a single I2C transaction at the specified address.
|
// Tx does a single I2C transaction at the specified address.
|
||||||
@@ -162,10 +162,10 @@ func (i2c I2C) Tx(addr uint16, w, r []byte) error {
|
|||||||
// start starts an I2C communication session.
|
// start starts an I2C communication session.
|
||||||
func (i2c I2C) start(address uint8, write bool) {
|
func (i2c I2C) start(address uint8, write bool) {
|
||||||
// Clear TWI interrupt flag, put start condition on SDA, and enable TWI.
|
// Clear TWI interrupt flag, put start condition on SDA, and enable TWI.
|
||||||
avr.TWCR.Set((avr.TWCR_TWINT | avr.TWCR_TWSTA | avr.TWCR_TWEN))
|
*avr.TWCR = (avr.TWCR_TWINT | avr.TWCR_TWSTA | avr.TWCR_TWEN)
|
||||||
|
|
||||||
// Wait till start condition is transmitted.
|
// Wait till start condition is transmitted.
|
||||||
for (avr.TWCR.Get() & avr.TWCR_TWINT) == 0 {
|
for (*avr.TWCR & avr.TWCR_TWINT) == 0 {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write 7-bit shifted peripheral address.
|
// Write 7-bit shifted peripheral address.
|
||||||
@@ -179,36 +179,36 @@ func (i2c I2C) start(address uint8, write bool) {
|
|||||||
// stop ends an I2C communication session.
|
// stop ends an I2C communication session.
|
||||||
func (i2c I2C) stop() {
|
func (i2c I2C) stop() {
|
||||||
// Send stop condition.
|
// Send stop condition.
|
||||||
avr.TWCR.Set(avr.TWCR_TWEN | avr.TWCR_TWINT | avr.TWCR_TWSTO)
|
*avr.TWCR = (avr.TWCR_TWEN | avr.TWCR_TWINT | avr.TWCR_TWSTO)
|
||||||
|
|
||||||
// Wait for stop condition to be executed on bus.
|
// Wait for stop condition to be executed on bus.
|
||||||
for (avr.TWCR.Get() & avr.TWCR_TWSTO) == 0 {
|
for (*avr.TWCR & avr.TWCR_TWSTO) == 0 {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// writeByte writes a single byte to the I2C bus.
|
// writeByte writes a single byte to the I2C bus.
|
||||||
func (i2c I2C) writeByte(data byte) {
|
func (i2c I2C) writeByte(data byte) {
|
||||||
// Write data to register.
|
// Write data to register.
|
||||||
avr.TWDR.Set(data)
|
*avr.TWDR = avr.RegValue(data)
|
||||||
|
|
||||||
// Clear TWI interrupt flag and enable TWI.
|
// Clear TWI interrupt flag and enable TWI.
|
||||||
avr.TWCR.Set(avr.TWCR_TWEN | avr.TWCR_TWINT)
|
*avr.TWCR = (avr.TWCR_TWEN | avr.TWCR_TWINT)
|
||||||
|
|
||||||
// Wait till data is transmitted.
|
// Wait till data is transmitted.
|
||||||
for (avr.TWCR.Get() & avr.TWCR_TWINT) == 0 {
|
for (*avr.TWCR & avr.TWCR_TWINT) == 0 {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// readByte reads a single byte from the I2C bus.
|
// readByte reads a single byte from the I2C bus.
|
||||||
func (i2c I2C) readByte() byte {
|
func (i2c I2C) readByte() byte {
|
||||||
// Clear TWI interrupt flag and enable TWI.
|
// Clear TWI interrupt flag and enable TWI.
|
||||||
avr.TWCR.Set(avr.TWCR_TWEN | avr.TWCR_TWINT | avr.TWCR_TWEA)
|
*avr.TWCR = (avr.TWCR_TWEN | avr.TWCR_TWINT | avr.TWCR_TWEA)
|
||||||
|
|
||||||
// Wait till read request is transmitted.
|
// Wait till read request is transmitted.
|
||||||
for (avr.TWCR.Get() & avr.TWCR_TWINT) == 0 {
|
for (*avr.TWCR & avr.TWCR_TWINT) == 0 {
|
||||||
}
|
}
|
||||||
|
|
||||||
return byte(avr.TWDR.Get())
|
return byte(*avr.TWDR)
|
||||||
}
|
}
|
||||||
|
|
||||||
// UART on the AVR.
|
// UART on the AVR.
|
||||||
@@ -226,32 +226,32 @@ func (uart UART) Configure(config UARTConfig) {
|
|||||||
// https://www.microchip.com/webdoc/AVRLibcReferenceManual/FAQ_1faq_wrong_baud_rate.html
|
// https://www.microchip.com/webdoc/AVRLibcReferenceManual/FAQ_1faq_wrong_baud_rate.html
|
||||||
// ((F_CPU + UART_BAUD_RATE * 8L) / (UART_BAUD_RATE * 16L) - 1)
|
// ((F_CPU + UART_BAUD_RATE * 8L) / (UART_BAUD_RATE * 16L) - 1)
|
||||||
ps := ((CPU_FREQUENCY+config.BaudRate*8)/(config.BaudRate*16) - 1)
|
ps := ((CPU_FREQUENCY+config.BaudRate*8)/(config.BaudRate*16) - 1)
|
||||||
avr.UBRR0H.Set(uint8(ps >> 8))
|
*avr.UBRR0H = avr.RegValue(ps >> 8)
|
||||||
avr.UBRR0L.Set(uint8(ps & 0xff))
|
*avr.UBRR0L = avr.RegValue(ps & 0xff)
|
||||||
|
|
||||||
// enable RX, TX and RX interrupt
|
// enable RX, TX and RX interrupt
|
||||||
avr.UCSR0B.Set(avr.UCSR0B_RXEN0 | avr.UCSR0B_TXEN0 | avr.UCSR0B_RXCIE0)
|
*avr.UCSR0B = avr.UCSR0B_RXEN0 | avr.UCSR0B_TXEN0 | avr.UCSR0B_RXCIE0
|
||||||
|
|
||||||
// 8-bits data
|
// 8-bits data
|
||||||
avr.UCSR0C.Set(avr.UCSR0C_UCSZ01 | avr.UCSR0C_UCSZ00)
|
*avr.UCSR0C = avr.UCSR0C_UCSZ01 | avr.UCSR0C_UCSZ00
|
||||||
}
|
}
|
||||||
|
|
||||||
// WriteByte writes a byte of data to the UART.
|
// WriteByte writes a byte of data to the UART.
|
||||||
func (uart UART) WriteByte(c byte) error {
|
func (uart UART) WriteByte(c byte) error {
|
||||||
// Wait until UART buffer is not busy.
|
// Wait until UART buffer is not busy.
|
||||||
for (avr.UCSR0A.Get() & avr.UCSR0A_UDRE0) == 0 {
|
for (*avr.UCSR0A & avr.UCSR0A_UDRE0) == 0 {
|
||||||
}
|
}
|
||||||
avr.UDR0.Set(c) // send char
|
*avr.UDR0 = avr.RegValue(c) // send char
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
//go:interrupt USART_RX_vect
|
//go:interrupt USART_RX_vect
|
||||||
func handleUSART_RX() {
|
func handleUSART_RX() {
|
||||||
// Read register to clear it.
|
// Read register to clear it.
|
||||||
data := avr.UDR0.Get()
|
data := *avr.UDR0
|
||||||
|
|
||||||
// Ensure no error.
|
// Ensure no error.
|
||||||
if (avr.UCSR0A.Get() & (avr.UCSR0A_FE0 | avr.UCSR0A_DOR0 | avr.UCSR0A_UPE0)) == 0 {
|
if (*avr.UCSR0A & (avr.UCSR0A_FE0 | avr.UCSR0A_DOR0 | avr.UCSR0A_UPE0)) == 0 {
|
||||||
// Put data from UDR register into buffer.
|
// Put data from UDR register into buffer.
|
||||||
UART0.Receive(byte(data))
|
UART0.Receive(byte(data))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -808,235 +808,6 @@ func (i2c I2C) readByte() byte {
|
|||||||
return byte(i2c.Bus.DATA)
|
return byte(i2c.Bus.DATA)
|
||||||
}
|
}
|
||||||
|
|
||||||
// I2S on the SAMD21.
|
|
||||||
|
|
||||||
// I2S
|
|
||||||
type I2S struct {
|
|
||||||
Bus *sam.I2S_Type
|
|
||||||
}
|
|
||||||
|
|
||||||
// Configure is used to configure the I2S interface. You must call this
|
|
||||||
// before you can use the I2S bus.
|
|
||||||
func (i2s I2S) Configure(config I2SConfig) {
|
|
||||||
// handle defaults
|
|
||||||
if config.SCK == 0 {
|
|
||||||
config.SCK = I2S_SCK_PIN
|
|
||||||
config.WS = I2S_WS_PIN
|
|
||||||
config.SD = I2S_SD_PIN
|
|
||||||
}
|
|
||||||
|
|
||||||
if config.AudioFrequency == 0 {
|
|
||||||
config.AudioFrequency = 48000
|
|
||||||
}
|
|
||||||
|
|
||||||
if config.DataFormat == I2SDataFormatDefault {
|
|
||||||
if config.Stereo {
|
|
||||||
config.DataFormat = I2SDataFormat16bit
|
|
||||||
} else {
|
|
||||||
config.DataFormat = I2SDataFormat32bit
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Turn on clock for I2S
|
|
||||||
sam.PM.APBCMASK |= sam.PM_APBCMASK_I2S_
|
|
||||||
|
|
||||||
// setting clock rate for sample.
|
|
||||||
division_factor := CPU_FREQUENCY / (config.AudioFrequency * uint32(config.DataFormat))
|
|
||||||
|
|
||||||
// Switch Generic Clock Generator 3 to DFLL48M.
|
|
||||||
sam.GCLK.GENDIV = sam.RegValue((sam.GCLK_CLKCTRL_GEN_GCLK3 << sam.GCLK_GENDIV_ID_Pos) |
|
|
||||||
(division_factor << sam.GCLK_GENDIV_DIV_Pos))
|
|
||||||
waitForSync()
|
|
||||||
|
|
||||||
sam.GCLK.GENCTRL = sam.RegValue((sam.GCLK_CLKCTRL_GEN_GCLK3 << sam.GCLK_GENCTRL_ID_Pos) |
|
|
||||||
(sam.GCLK_GENCTRL_SRC_DFLL48M << sam.GCLK_GENCTRL_SRC_Pos) |
|
|
||||||
sam.GCLK_GENCTRL_IDC |
|
|
||||||
sam.GCLK_GENCTRL_GENEN)
|
|
||||||
waitForSync()
|
|
||||||
|
|
||||||
// Use Generic Clock Generator 3 as source for I2S.
|
|
||||||
sam.GCLK.CLKCTRL = sam.RegValue16((sam.GCLK_CLKCTRL_ID_I2S_0 << sam.GCLK_CLKCTRL_ID_Pos) |
|
|
||||||
(sam.GCLK_CLKCTRL_GEN_GCLK3 << sam.GCLK_CLKCTRL_GEN_Pos) |
|
|
||||||
sam.GCLK_CLKCTRL_CLKEN)
|
|
||||||
waitForSync()
|
|
||||||
|
|
||||||
// reset the device
|
|
||||||
i2s.Bus.CTRLA |= sam.I2S_CTRLA_SWRST
|
|
||||||
for (i2s.Bus.SYNCBUSY & sam.I2S_SYNCBUSY_SWRST) > 0 {
|
|
||||||
}
|
|
||||||
|
|
||||||
// disable device before continuing
|
|
||||||
for (i2s.Bus.SYNCBUSY & sam.I2S_SYNCBUSY_ENABLE) > 0 {
|
|
||||||
}
|
|
||||||
i2s.Bus.CTRLA &^= sam.I2S_CTRLA_ENABLE
|
|
||||||
|
|
||||||
// setup clock
|
|
||||||
if config.ClockSource == I2SClockSourceInternal {
|
|
||||||
// TODO: make sure correct for I2S output
|
|
||||||
|
|
||||||
// set serial clock select pin
|
|
||||||
i2s.Bus.CLKCTRL0 |= sam.I2S_CLKCTRL_SCKSEL
|
|
||||||
|
|
||||||
// set frame select pin
|
|
||||||
i2s.Bus.CLKCTRL0 |= sam.I2S_CLKCTRL_FSSEL
|
|
||||||
} else {
|
|
||||||
// Configure FS generation from SCK clock.
|
|
||||||
i2s.Bus.CLKCTRL0 &^= sam.I2S_CLKCTRL_FSSEL
|
|
||||||
}
|
|
||||||
|
|
||||||
if config.Standard == I2StandardPhilips {
|
|
||||||
// set 1-bit delay
|
|
||||||
i2s.Bus.CLKCTRL0 |= sam.I2S_CLKCTRL_BITDELAY
|
|
||||||
} else {
|
|
||||||
// set 0-bit delay
|
|
||||||
i2s.Bus.CLKCTRL0 &^= sam.I2S_CLKCTRL_BITDELAY
|
|
||||||
}
|
|
||||||
|
|
||||||
// set number of slots.
|
|
||||||
if config.Stereo {
|
|
||||||
i2s.Bus.CLKCTRL0 |= (1 << sam.I2S_CLKCTRL_NBSLOTS_Pos)
|
|
||||||
} else {
|
|
||||||
i2s.Bus.CLKCTRL0 &^= (1 << sam.I2S_CLKCTRL_NBSLOTS_Pos)
|
|
||||||
}
|
|
||||||
|
|
||||||
// set slot size
|
|
||||||
switch config.DataFormat {
|
|
||||||
case I2SDataFormat8bit:
|
|
||||||
i2s.Bus.CLKCTRL0 |= sam.I2S_CLKCTRL_SLOTSIZE_8
|
|
||||||
|
|
||||||
case I2SDataFormat16bit:
|
|
||||||
i2s.Bus.CLKCTRL0 |= sam.I2S_CLKCTRL_SLOTSIZE_16
|
|
||||||
|
|
||||||
case I2SDataFormat24bit:
|
|
||||||
i2s.Bus.CLKCTRL0 |= sam.I2S_CLKCTRL_SLOTSIZE_24
|
|
||||||
|
|
||||||
case I2SDataFormat32bit:
|
|
||||||
i2s.Bus.CLKCTRL0 |= sam.I2S_CLKCTRL_SLOTSIZE_32
|
|
||||||
}
|
|
||||||
|
|
||||||
// configure pin for clock
|
|
||||||
GPIO{config.SCK}.Configure(GPIOConfig{Mode: GPIO_COM})
|
|
||||||
|
|
||||||
// configure pin for WS, if needed
|
|
||||||
if config.WS != 0xff {
|
|
||||||
GPIO{config.WS}.Configure(GPIOConfig{Mode: GPIO_COM})
|
|
||||||
}
|
|
||||||
|
|
||||||
// now set serializer data size.
|
|
||||||
switch config.DataFormat {
|
|
||||||
case I2SDataFormat8bit:
|
|
||||||
i2s.Bus.SERCTRL1 |= sam.I2S_SERCTRL_DATASIZE_8
|
|
||||||
|
|
||||||
case I2SDataFormat16bit:
|
|
||||||
i2s.Bus.SERCTRL1 |= sam.I2S_SERCTRL_DATASIZE_16
|
|
||||||
|
|
||||||
case I2SDataFormat24bit:
|
|
||||||
i2s.Bus.SERCTRL1 |= sam.I2S_SERCTRL_DATASIZE_24
|
|
||||||
|
|
||||||
case I2SDataFormat32bit:
|
|
||||||
case I2SDataFormatDefault:
|
|
||||||
i2s.Bus.SERCTRL1 |= sam.I2S_SERCTRL_DATASIZE_32
|
|
||||||
}
|
|
||||||
|
|
||||||
// set serializer slot adjustment
|
|
||||||
if config.Standard == I2SStandardLSB {
|
|
||||||
// adjust right
|
|
||||||
i2s.Bus.SERCTRL1 &^= sam.I2S_SERCTRL_SLOTADJ
|
|
||||||
} else {
|
|
||||||
// adjust left
|
|
||||||
i2s.Bus.SERCTRL1 |= sam.I2S_SERCTRL_SLOTADJ
|
|
||||||
|
|
||||||
// reverse bit order?
|
|
||||||
i2s.Bus.SERCTRL1 |= sam.I2S_SERCTRL_BITREV
|
|
||||||
}
|
|
||||||
|
|
||||||
// set serializer mode.
|
|
||||||
if config.Mode == I2SModePDM {
|
|
||||||
i2s.Bus.SERCTRL1 |= sam.I2S_SERCTRL_SERMODE_PDM2
|
|
||||||
} else {
|
|
||||||
i2s.Bus.SERCTRL1 |= sam.I2S_SERCTRL_SERMODE_RX
|
|
||||||
}
|
|
||||||
|
|
||||||
// configure data pin
|
|
||||||
GPIO{config.SD}.Configure(GPIOConfig{Mode: GPIO_COM})
|
|
||||||
|
|
||||||
// re-enable
|
|
||||||
i2s.Bus.CTRLA |= sam.I2S_CTRLA_ENABLE
|
|
||||||
for (i2s.Bus.SYNCBUSY & sam.I2S_SYNCBUSY_ENABLE) > 0 {
|
|
||||||
}
|
|
||||||
|
|
||||||
// enable i2s clock
|
|
||||||
i2s.Bus.CTRLA |= sam.I2S_CTRLA_CKEN0
|
|
||||||
for (i2s.Bus.SYNCBUSY & sam.I2S_SYNCBUSY_CKEN0) > 0 {
|
|
||||||
}
|
|
||||||
|
|
||||||
// enable i2s serializer
|
|
||||||
i2s.Bus.CTRLA |= sam.I2S_CTRLA_SEREN1
|
|
||||||
for (i2s.Bus.SYNCBUSY & sam.I2S_SYNCBUSY_SEREN1) > 0 {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read data from the I2S bus into the provided slice.
|
|
||||||
// The I2S bus must already have been configured correctly.
|
|
||||||
func (i2s I2S) Read(p []uint32) (n int, err error) {
|
|
||||||
i := 0
|
|
||||||
for i = 0; i < len(p); i++ {
|
|
||||||
// Wait until ready
|
|
||||||
for (i2s.Bus.INTFLAG & sam.I2S_INTFLAG_RXRDY1) == 0 {
|
|
||||||
}
|
|
||||||
|
|
||||||
for (i2s.Bus.SYNCBUSY & sam.I2S_SYNCBUSY_DATA1) > 0 {
|
|
||||||
}
|
|
||||||
|
|
||||||
// read data
|
|
||||||
p[i] = uint32(i2s.Bus.DATA1)
|
|
||||||
|
|
||||||
// indicate read complete
|
|
||||||
i2s.Bus.INTFLAG = sam.I2S_INTFLAG_RXRDY1
|
|
||||||
}
|
|
||||||
|
|
||||||
return i, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Write data to the I2S bus from the provided slice.
|
|
||||||
// The I2S bus must already have been configured correctly.
|
|
||||||
func (i2s I2S) Write(p []uint32) (n int, err error) {
|
|
||||||
i := 0
|
|
||||||
for i = 0; i < len(p); i++ {
|
|
||||||
// Wait until ready
|
|
||||||
for (i2s.Bus.INTFLAG & sam.I2S_INTFLAG_TXRDY1) == 0 {
|
|
||||||
}
|
|
||||||
|
|
||||||
for (i2s.Bus.SYNCBUSY & sam.I2S_SYNCBUSY_DATA1) > 0 {
|
|
||||||
}
|
|
||||||
|
|
||||||
// write data
|
|
||||||
i2s.Bus.DATA1 = sam.RegValue(p[i])
|
|
||||||
|
|
||||||
// indicate write complete
|
|
||||||
i2s.Bus.INTFLAG = sam.I2S_INTFLAG_TXRDY1
|
|
||||||
}
|
|
||||||
|
|
||||||
return i, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Close the I2S bus.
|
|
||||||
func (i2s I2S) Close() error {
|
|
||||||
// Sync wait
|
|
||||||
for (i2s.Bus.SYNCBUSY & sam.I2S_SYNCBUSY_ENABLE) > 0 {
|
|
||||||
}
|
|
||||||
|
|
||||||
// disable I2S
|
|
||||||
i2s.Bus.CTRLA &^= sam.I2S_CTRLA_ENABLE
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func waitForSync() {
|
|
||||||
for (sam.GCLK.STATUS & sam.GCLK_STATUS_SYNCBUSY) > 0 {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// SPI
|
// SPI
|
||||||
type SPI struct {
|
type SPI struct {
|
||||||
Bus *sam.SERCOM_SPI_Type
|
Bus *sam.SERCOM_SPI_Type
|
||||||
|
|||||||
@@ -9,19 +9,19 @@ import (
|
|||||||
// Configure sets the pin to input or output.
|
// Configure sets the pin to input or output.
|
||||||
func (p GPIO) Configure(config GPIOConfig) {
|
func (p GPIO) Configure(config GPIOConfig) {
|
||||||
if config.Mode == GPIO_OUTPUT { // set output bit
|
if config.Mode == GPIO_OUTPUT { // set output bit
|
||||||
avr.DDRB.SetBits(1 << p.Pin)
|
*avr.DDRB |= 1 << p.Pin
|
||||||
} else { // configure input: clear output bit
|
} else { // configure input: clear output bit
|
||||||
avr.DDRB.ClearBits(1 << p.Pin)
|
*avr.DDRB &^= 1 << p.Pin
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p GPIO) getPortMask() (*avr.Register8, uint8) {
|
func (p GPIO) getPortMask() (*avr.RegValue, uint8) {
|
||||||
return avr.PORTB, 1 << p.Pin
|
return avr.PORTB, 1 << p.Pin
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get returns the current value of a GPIO pin.
|
// Get returns the current value of a GPIO pin.
|
||||||
func (p GPIO) Get() bool {
|
func (p GPIO) Get() bool {
|
||||||
val := avr.PINB.Get() & (1 << p.Pin)
|
val := *avr.PINB & (1 << p.Pin)
|
||||||
return (val > 0)
|
return (val > 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+14
-12
@@ -17,10 +17,10 @@ const (
|
|||||||
func (p GPIO) Set(value bool) {
|
func (p GPIO) Set(value bool) {
|
||||||
if value { // set bits
|
if value { // set bits
|
||||||
port, mask := p.PortMaskSet()
|
port, mask := p.PortMaskSet()
|
||||||
port.Set(mask)
|
*port = mask
|
||||||
} else { // clear bits
|
} else { // clear bits
|
||||||
port, mask := p.PortMaskClear()
|
port, mask := p.PortMaskClear()
|
||||||
port.Set(mask)
|
*port = mask
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -30,9 +30,9 @@ func (p GPIO) Set(value bool) {
|
|||||||
// Warning: there are no separate pin set/clear registers on the AVR. The
|
// Warning: there are no separate pin set/clear registers on the AVR. The
|
||||||
// returned mask is only valid as long as no other pin in the same port has been
|
// returned mask is only valid as long as no other pin in the same port has been
|
||||||
// changed.
|
// changed.
|
||||||
func (p GPIO) PortMaskSet() (*avr.Register8, uint8) {
|
func (p GPIO) PortMaskSet() (*avr.RegValue, avr.RegValue) {
|
||||||
port, mask := p.getPortMask()
|
port, mask := p.getPortMask()
|
||||||
return port, port.Get() | mask
|
return port, *port | avr.RegValue(mask)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return the register and mask to disable a given port. This can be used to
|
// Return the register and mask to disable a given port. This can be used to
|
||||||
@@ -41,18 +41,18 @@ func (p GPIO) PortMaskSet() (*avr.Register8, uint8) {
|
|||||||
// Warning: there are no separate pin set/clear registers on the AVR. The
|
// Warning: there are no separate pin set/clear registers on the AVR. The
|
||||||
// returned mask is only valid as long as no other pin in the same port has been
|
// returned mask is only valid as long as no other pin in the same port has been
|
||||||
// changed.
|
// changed.
|
||||||
func (p GPIO) PortMaskClear() (*avr.Register8, uint8) {
|
func (p GPIO) PortMaskClear() (*avr.RegValue, avr.RegValue) {
|
||||||
port, mask := p.getPortMask()
|
port, mask := p.getPortMask()
|
||||||
return port, port.Get() &^ mask
|
return port, *port &^ avr.RegValue(mask)
|
||||||
}
|
}
|
||||||
|
|
||||||
// InitADC initializes the registers needed for ADC.
|
// InitADC initializes the registers needed for ADC.
|
||||||
func InitADC() {
|
func InitADC() {
|
||||||
// set a2d prescaler so we are inside the desired 50-200 KHz range at 16MHz.
|
// set a2d prescaler so we are inside the desired 50-200 KHz range at 16MHz.
|
||||||
avr.ADCSRA.SetBits(avr.ADCSRA_ADPS2 | avr.ADCSRA_ADPS1 | avr.ADCSRA_ADPS0)
|
*avr.ADCSRA |= (avr.ADCSRA_ADPS2 | avr.ADCSRA_ADPS1 | avr.ADCSRA_ADPS0)
|
||||||
|
|
||||||
// enable a2d conversions
|
// enable a2d conversions
|
||||||
avr.ADCSRA.SetBits(avr.ADCSRA_ADEN)
|
*avr.ADCSRA |= avr.ADCSRA_ADEN
|
||||||
}
|
}
|
||||||
|
|
||||||
// Configure configures a ADCPin to be able to be used to read data.
|
// Configure configures a ADCPin to be able to be used to read data.
|
||||||
@@ -68,16 +68,18 @@ func (a ADC) Get() uint16 {
|
|||||||
// set the ADLAR bit (left-adjusted result) to get a value scaled to 16
|
// set the ADLAR bit (left-adjusted result) to get a value scaled to 16
|
||||||
// bits. This has the same effect as shifting the return value left by 6
|
// bits. This has the same effect as shifting the return value left by 6
|
||||||
// bits.
|
// bits.
|
||||||
avr.ADMUX.Set(avr.ADMUX_REFS0 | avr.ADMUX_ADLAR | (a.Pin & 0x07))
|
*avr.ADMUX = avr.RegValue(avr.ADMUX_REFS0 | avr.ADMUX_ADLAR | (a.Pin & 0x07))
|
||||||
|
|
||||||
// start the conversion
|
// start the conversion
|
||||||
avr.ADCSRA.SetBits(avr.ADCSRA_ADSC)
|
*avr.ADCSRA |= avr.ADCSRA_ADSC
|
||||||
|
|
||||||
// ADSC is cleared when the conversion finishes
|
// ADSC is cleared when the conversion finishes
|
||||||
for ok := true; ok; ok = (avr.ADCSRA.Get() & avr.ADCSRA_ADSC) > 0 {
|
for ok := true; ok; ok = (*avr.ADCSRA & avr.ADCSRA_ADSC) > 0 {
|
||||||
}
|
}
|
||||||
|
|
||||||
return uint16(avr.ADCL.Get()) | uint16(avr.ADCH.Get())<<8
|
low := uint16(*avr.ADCL)
|
||||||
|
high := uint16(*avr.ADCH)
|
||||||
|
return uint16(low) | uint16(high<<8)
|
||||||
}
|
}
|
||||||
|
|
||||||
// I2C on AVR.
|
// I2C on AVR.
|
||||||
|
|||||||
@@ -12,29 +12,77 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// Fake LED numbers, for testing.
|
// Fake LED numbers, for testing.
|
||||||
const (
|
var (
|
||||||
LED = LED1
|
LED uint8 = LED1
|
||||||
LED1 = 0
|
LED1 uint8 = 0
|
||||||
LED2 = 0
|
LED2 uint8 = 0
|
||||||
LED3 = 0
|
LED3 uint8 = 0
|
||||||
LED4 = 0
|
LED4 uint8 = 0
|
||||||
)
|
)
|
||||||
|
|
||||||
// Fake button numbers, for testing.
|
// Fake button numbers, for testing.
|
||||||
const (
|
var (
|
||||||
BUTTON = BUTTON1
|
BUTTON uint8 = BUTTON1
|
||||||
BUTTON1 = 0
|
BUTTON1 uint8 = 5
|
||||||
BUTTON2 = 0
|
BUTTON2 uint8 = 6
|
||||||
BUTTON3 = 0
|
BUTTON3 uint8 = 7
|
||||||
BUTTON4 = 0
|
BUTTON4 uint8 = 8
|
||||||
|
)
|
||||||
|
|
||||||
|
// Fake SPI interfaces, for testing.
|
||||||
|
var (
|
||||||
|
SPI0 = SPI{0}
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
GPIOConfigure func(pin uint8, config GPIOConfig)
|
||||||
|
GPIOSet func(pin uint8, value bool)
|
||||||
|
GPIOGet func(pin uint8) bool
|
||||||
|
SPIConfigure func(bus uint8, sck uint8, mosi uint8, miso uint8)
|
||||||
|
SPITransfer func(bus uint8, w uint8) uint8
|
||||||
)
|
)
|
||||||
|
|
||||||
func (p GPIO) Configure(config GPIOConfig) {
|
func (p GPIO) Configure(config GPIOConfig) {
|
||||||
|
if GPIOConfigure != nil {
|
||||||
|
GPIOConfigure(p.Pin, config)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p GPIO) Set(value bool) {
|
func (p GPIO) Set(value bool) {
|
||||||
|
if GPIOSet != nil {
|
||||||
|
GPIOSet(p.Pin, value)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p GPIO) Get() bool {
|
func (p GPIO) Get() bool {
|
||||||
|
if GPIOGet != nil {
|
||||||
|
return GPIOGet(p.Pin)
|
||||||
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type SPI struct {
|
||||||
|
Bus uint8
|
||||||
|
}
|
||||||
|
|
||||||
|
type SPIConfig struct {
|
||||||
|
Frequency uint32
|
||||||
|
SCK uint8
|
||||||
|
MOSI uint8
|
||||||
|
MISO uint8
|
||||||
|
Mode uint8
|
||||||
|
}
|
||||||
|
|
||||||
|
func (spi SPI) Configure(config SPIConfig) {
|
||||||
|
if SPIConfigure != nil {
|
||||||
|
SPIConfigure(spi.Bus, config.SCK, config.MOSI, config.MISO)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Transfer writes/reads a single byte using the SPI interface.
|
||||||
|
func (spi SPI) Transfer(w byte) (byte, error) {
|
||||||
|
if SPITransfer != nil {
|
||||||
|
return SPITransfer(spi.Bus, w), nil
|
||||||
|
}
|
||||||
|
return 0, nil
|
||||||
|
}
|
||||||
|
|||||||
@@ -185,20 +185,21 @@ func (i2c I2C) Tx(addr uint16, w, r []byte) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if len(r) != 0 {
|
if len(r) != 0 {
|
||||||
// To trigger suspend task when a byte is received
|
|
||||||
i2c.Bus.SHORTS = nrf.TWI_SHORTS_BB_SUSPEND
|
|
||||||
i2c.Bus.TASKS_STARTRX = 1 // re-start transmission for reading
|
i2c.Bus.TASKS_STARTRX = 1 // re-start transmission for reading
|
||||||
for i := range r { // read each char
|
for i := range r { // read each char
|
||||||
if i+1 == len(r) {
|
if i+1 == len(r) {
|
||||||
// To trigger stop task when last byte is received, set before resume task.
|
// The 'stop' signal must be sent before reading back the last
|
||||||
i2c.Bus.SHORTS = nrf.TWI_SHORTS_BB_STOP
|
// byte, so that it will be sent by the I2C peripheral right
|
||||||
|
// after the last byte has been read.
|
||||||
|
r[i] = i2c.readLastByte()
|
||||||
|
} else {
|
||||||
|
r[i] = i2c.readByte()
|
||||||
}
|
}
|
||||||
i2c.Bus.TASKS_RESUME = 1 // re-start transmission for reading
|
|
||||||
r[i] = i2c.readByte()
|
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
// Nothing to read back. Stop the transmission.
|
||||||
|
i2c.signalStop()
|
||||||
}
|
}
|
||||||
i2c.signalStop()
|
|
||||||
i2c.Bus.SHORTS = nrf.TWI_SHORTS_BB_SUSPEND_Disabled
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -228,6 +229,16 @@ func (i2c I2C) readByte() byte {
|
|||||||
return byte(i2c.Bus.RXD)
|
return byte(i2c.Bus.RXD)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// readLastByte reads a single byte from the I2C bus, sending a stop signal
|
||||||
|
// after it has been read.
|
||||||
|
func (i2c I2C) readLastByte() byte {
|
||||||
|
for i2c.Bus.EVENTS_RXDREADY == 0 {
|
||||||
|
}
|
||||||
|
i2c.Bus.EVENTS_RXDREADY = 0
|
||||||
|
i2c.signalStop() // signal 'stop' now, so it is sent when reading RXD
|
||||||
|
return byte(i2c.Bus.RXD)
|
||||||
|
}
|
||||||
|
|
||||||
// SPI on the NRF.
|
// SPI on the NRF.
|
||||||
type SPI struct {
|
type SPI struct {
|
||||||
Bus *nrf.SPI_Type
|
Bus *nrf.SPI_Type
|
||||||
|
|||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
// +build nrf stm32f103xx atsamd21g18a
|
// +build js,wasm nrf stm32f103xx atsamd21g18a
|
||||||
|
|
||||||
package machine
|
package machine
|
||||||
|
|
||||||
|
|||||||
@@ -1,91 +0,0 @@
|
|||||||
// +build gc.conservative
|
|
||||||
|
|
||||||
package runtime
|
|
||||||
|
|
||||||
import (
|
|
||||||
"unsafe"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Initialize the memory allocator.
|
|
||||||
// No memory may be allocated before this is called. That means the runtime and
|
|
||||||
// any packages the runtime depends upon may not allocate memory during package
|
|
||||||
// initialization.
|
|
||||||
func init() {
|
|
||||||
totalSize := heapEnd - heapStart
|
|
||||||
|
|
||||||
// Allocate some memory to keep 2 bits of information about every block.
|
|
||||||
metadataSize := totalSize / (blocksPerStateByte * bytesPerBlock)
|
|
||||||
|
|
||||||
// Align the pool.
|
|
||||||
poolStart = (heapStart + metadataSize + (bytesPerBlock - 1)) &^ (bytesPerBlock - 1)
|
|
||||||
poolEnd := heapEnd &^ (bytesPerBlock - 1)
|
|
||||||
numBlocks := (poolEnd - poolStart) / bytesPerBlock
|
|
||||||
endBlock = gcBlock(numBlocks)
|
|
||||||
if gcDebug {
|
|
||||||
println("heapStart: ", heapStart)
|
|
||||||
println("heapEnd: ", heapEnd)
|
|
||||||
println("total size: ", totalSize)
|
|
||||||
println("metadata size: ", metadataSize)
|
|
||||||
println("poolStart: ", poolStart)
|
|
||||||
println("# of blocks: ", numBlocks)
|
|
||||||
println("# of block states:", metadataSize*blocksPerStateByte)
|
|
||||||
}
|
|
||||||
if gcAsserts && metadataSize*blocksPerStateByte < numBlocks {
|
|
||||||
// sanity check
|
|
||||||
runtimePanic("gc: metadata array is too small")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set all block states to 'free'.
|
|
||||||
memzero(unsafe.Pointer(heapStart), metadataSize)
|
|
||||||
}
|
|
||||||
|
|
||||||
func alloc(size uintptr) unsafe.Pointer {
|
|
||||||
return heapAlloc(size)
|
|
||||||
}
|
|
||||||
|
|
||||||
// GC performs a garbage collection cycle.
|
|
||||||
func GC() {
|
|
||||||
if gcDebug {
|
|
||||||
println("running collection cycle...")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Mark phase: mark all reachable objects, recursively.
|
|
||||||
markRoots(globalsStart, globalsEnd)
|
|
||||||
markRoots(getCurrentStackPointer(), stackTop) // assume a descending stack
|
|
||||||
|
|
||||||
// Sweep phase: free all non-marked objects and unmark marked objects for
|
|
||||||
// the next collection cycle.
|
|
||||||
sweep()
|
|
||||||
|
|
||||||
// Show how much has been sweeped, for debugging.
|
|
||||||
if gcDebug {
|
|
||||||
dumpHeap()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// markRoots reads all pointers from start to end (exclusive) and if they look
|
|
||||||
// like a heap pointer and are unmarked, marks them and scans that object as
|
|
||||||
// well (recursively). The start and end parameters must be valid pointers and
|
|
||||||
// must be aligned.
|
|
||||||
func markRoots(start, end uintptr) {
|
|
||||||
if gcDebug {
|
|
||||||
println("mark from", start, "to", end, int(end-start))
|
|
||||||
}
|
|
||||||
|
|
||||||
for addr := start; addr != end; addr += unsafe.Sizeof(addr) {
|
|
||||||
root := *(*uintptr)(unsafe.Pointer(addr))
|
|
||||||
if addressOnHeap(root) {
|
|
||||||
block := blockFromAddr(root)
|
|
||||||
head := block.findHead()
|
|
||||||
if head.state() != blockStateMark {
|
|
||||||
if gcDebug {
|
|
||||||
println("found unmarked pointer", root, "at address", addr)
|
|
||||||
}
|
|
||||||
head.setState(blockStateMark)
|
|
||||||
next := block.findNext()
|
|
||||||
// TODO: avoid recursion as much as possible
|
|
||||||
markRoots(head.address(), next.address())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
// +build gc.leaking
|
// +build gc.dumb
|
||||||
|
|
||||||
package runtime
|
package runtime
|
||||||
|
|
||||||
@@ -30,6 +30,18 @@ func alloc(size uintptr) unsafe.Pointer {
|
|||||||
return unsafe.Pointer(addr)
|
return unsafe.Pointer(addr)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func free(ptr unsafe.Pointer) {
|
||||||
|
// Memory is never freed.
|
||||||
|
}
|
||||||
|
|
||||||
func GC() {
|
func GC() {
|
||||||
// No-op.
|
// No-op.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func KeepAlive(x interface{}) {
|
||||||
|
// Unimplemented. Only required with SetFinalizer().
|
||||||
|
}
|
||||||
|
|
||||||
|
func SetFinalizer(obj interface{}, finalizer interface{}) {
|
||||||
|
// Unimplemented.
|
||||||
|
}
|
||||||
@@ -1,3 +1,5 @@
|
|||||||
|
// +build gc.marksweep
|
||||||
|
|
||||||
package runtime
|
package runtime
|
||||||
|
|
||||||
// This memory manager is a textbook mark/sweep implementation, heavily inspired
|
// This memory manager is a textbook mark/sweep implementation, heavily inspired
|
||||||
@@ -166,9 +168,42 @@ func (b gcBlock) unmark() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Initialize the memory allocator.
|
||||||
|
// No memory may be allocated before this is called. That means the runtime and
|
||||||
|
// any packages the runtime depends upon may not allocate memory during package
|
||||||
|
// initialization.
|
||||||
|
func init() {
|
||||||
|
totalSize := heapEnd - heapStart
|
||||||
|
|
||||||
|
// Allocate some memory to keep 2 bits of information about every block.
|
||||||
|
metadataSize := totalSize / (blocksPerStateByte * bytesPerBlock)
|
||||||
|
|
||||||
|
// Align the pool.
|
||||||
|
poolStart = (heapStart + metadataSize + (bytesPerBlock - 1)) &^ (bytesPerBlock - 1)
|
||||||
|
poolEnd := heapEnd &^ (bytesPerBlock - 1)
|
||||||
|
numBlocks := (poolEnd - poolStart) / bytesPerBlock
|
||||||
|
endBlock = gcBlock(numBlocks)
|
||||||
|
if gcDebug {
|
||||||
|
println("heapStart: ", heapStart)
|
||||||
|
println("heapEnd: ", heapEnd)
|
||||||
|
println("total size: ", totalSize)
|
||||||
|
println("metadata size: ", metadataSize)
|
||||||
|
println("poolStart: ", poolStart)
|
||||||
|
println("# of blocks: ", numBlocks)
|
||||||
|
println("# of block states:", metadataSize*blocksPerStateByte)
|
||||||
|
}
|
||||||
|
if gcAsserts && metadataSize*blocksPerStateByte < numBlocks {
|
||||||
|
// sanity check
|
||||||
|
runtimePanic("gc: metadata array is too small")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set all block states to 'free'.
|
||||||
|
memzero(unsafe.Pointer(heapStart), metadataSize)
|
||||||
|
}
|
||||||
|
|
||||||
// alloc tries to find some free space on the heap, possibly doing a garbage
|
// alloc tries to find some free space on the heap, possibly doing a garbage
|
||||||
// collection cycle if needed. If no space is free, it panics.
|
// collection cycle if needed. If no space is free, it panics.
|
||||||
func heapAlloc(size uintptr) unsafe.Pointer {
|
func alloc(size uintptr) unsafe.Pointer {
|
||||||
if size == 0 {
|
if size == 0 {
|
||||||
return unsafe.Pointer(&zeroSizedAlloc)
|
return unsafe.Pointer(&zeroSizedAlloc)
|
||||||
}
|
}
|
||||||
@@ -240,6 +275,53 @@ func free(ptr unsafe.Pointer) {
|
|||||||
// TODO: free blocks on request, when the compiler knows they're unused.
|
// TODO: free blocks on request, when the compiler knows they're unused.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GC performs a garbage collection cycle.
|
||||||
|
func GC() {
|
||||||
|
if gcDebug {
|
||||||
|
println("running collection cycle...")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mark phase: mark all reachable objects, recursively.
|
||||||
|
markRoots(globalsStart, globalsEnd)
|
||||||
|
markRoots(getCurrentStackPointer(), stackTop) // assume a descending stack
|
||||||
|
|
||||||
|
// Sweep phase: free all non-marked objects and unmark marked objects for
|
||||||
|
// the next collection cycle.
|
||||||
|
sweep()
|
||||||
|
|
||||||
|
// Show how much has been sweeped, for debugging.
|
||||||
|
if gcDebug {
|
||||||
|
dumpHeap()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// markRoots reads all pointers from start to end (exclusive) and if they look
|
||||||
|
// like a heap pointer and are unmarked, marks them and scans that object as
|
||||||
|
// well (recursively). The start and end parameters must be valid pointers and
|
||||||
|
// must be aligned.
|
||||||
|
func markRoots(start, end uintptr) {
|
||||||
|
if gcDebug {
|
||||||
|
println("mark from", start, "to", end, int(end-start))
|
||||||
|
}
|
||||||
|
|
||||||
|
for addr := start; addr != end; addr += unsafe.Sizeof(addr) {
|
||||||
|
root := *(*uintptr)(unsafe.Pointer(addr))
|
||||||
|
if looksLikePointer(root) {
|
||||||
|
block := blockFromAddr(root)
|
||||||
|
head := block.findHead()
|
||||||
|
if head.state() != blockStateMark {
|
||||||
|
if gcDebug {
|
||||||
|
println("found unmarked pointer", root, "at address", addr)
|
||||||
|
}
|
||||||
|
head.setState(blockStateMark)
|
||||||
|
next := block.findNext()
|
||||||
|
// TODO: avoid recursion as much as possible
|
||||||
|
markRoots(head.address(), next.address())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Sweep goes through all memory and frees unmarked memory.
|
// Sweep goes through all memory and frees unmarked memory.
|
||||||
func sweep() {
|
func sweep() {
|
||||||
freeCurrentObject := false
|
freeCurrentObject := false
|
||||||
@@ -265,8 +347,10 @@ func sweep() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// addressOnHeap returns whether this address points into the heap.
|
// looksLikePointer returns whether this could be a pointer. Currently, it
|
||||||
func addressOnHeap(ptr uintptr) bool {
|
// simply returns whether it lies anywhere in the heap. Go allows interior
|
||||||
|
// pointers so we can't check alignment or anything like that.
|
||||||
|
func looksLikePointer(ptr uintptr) bool {
|
||||||
return ptr >= poolStart && ptr < heapEnd
|
return ptr >= poolStart && ptr < heapEnd
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -12,6 +12,18 @@ import (
|
|||||||
|
|
||||||
func alloc(size uintptr) unsafe.Pointer
|
func alloc(size uintptr) unsafe.Pointer
|
||||||
|
|
||||||
|
func free(ptr unsafe.Pointer) {
|
||||||
|
// Nothing to free when nothing gets allocated.
|
||||||
|
}
|
||||||
|
|
||||||
func GC() {
|
func GC() {
|
||||||
// Unimplemented.
|
// Unimplemented.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func KeepAlive(x interface{}) {
|
||||||
|
// Unimplemented. Only required with SetFinalizer().
|
||||||
|
}
|
||||||
|
|
||||||
|
func SetFinalizer(obj interface{}, finalizer interface{}) {
|
||||||
|
// Unimplemented.
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,116 +0,0 @@
|
|||||||
// +build gc.precise
|
|
||||||
|
|
||||||
package runtime
|
|
||||||
|
|
||||||
import (
|
|
||||||
"unsafe"
|
|
||||||
)
|
|
||||||
|
|
||||||
//go:extern runtime.trackedGlobalsStart
|
|
||||||
var trackedGlobalsStart uintptr
|
|
||||||
|
|
||||||
//go:extern runtime.trackedGlobalsLength
|
|
||||||
var trackedGlobalsLength uintptr
|
|
||||||
|
|
||||||
//go:extern runtime.trackedGlobalsBitmap
|
|
||||||
var trackedGlobalsBitmap [0]uint8
|
|
||||||
|
|
||||||
// Initialize the memory allocator.
|
|
||||||
// No memory may be allocated before this is called. That means the runtime and
|
|
||||||
// any packages the runtime depends upon may not allocate memory during package
|
|
||||||
// initialization.
|
|
||||||
func init() {
|
|
||||||
totalSize := heapEnd - heapStart
|
|
||||||
|
|
||||||
// Allocate some memory to keep 2 bits of information about every block.
|
|
||||||
metadataSize := totalSize / (blocksPerStateByte * bytesPerBlock)
|
|
||||||
|
|
||||||
// Align the pool.
|
|
||||||
poolStart = (heapStart + metadataSize + (bytesPerBlock - 1)) &^ (bytesPerBlock - 1)
|
|
||||||
poolEnd := heapEnd &^ (bytesPerBlock - 1)
|
|
||||||
numBlocks := (poolEnd - poolStart) / bytesPerBlock
|
|
||||||
endBlock = gcBlock(numBlocks)
|
|
||||||
if gcDebug {
|
|
||||||
println("heapStart: ", heapStart)
|
|
||||||
println("heapEnd: ", heapEnd)
|
|
||||||
println("total size: ", totalSize)
|
|
||||||
println("metadata size: ", metadataSize)
|
|
||||||
println("poolStart: ", poolStart)
|
|
||||||
println("# of blocks: ", numBlocks)
|
|
||||||
println("# of block states:", metadataSize*blocksPerStateByte)
|
|
||||||
}
|
|
||||||
if gcAsserts && metadataSize*blocksPerStateByte < numBlocks {
|
|
||||||
// sanity check
|
|
||||||
runtimePanic("gc: metadata array is too small")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set all block states to 'free'.
|
|
||||||
memzero(unsafe.Pointer(heapStart), metadataSize)
|
|
||||||
}
|
|
||||||
|
|
||||||
func alloc(size uintptr) unsafe.Pointer {
|
|
||||||
GC()
|
|
||||||
return heapAlloc(size)
|
|
||||||
}
|
|
||||||
|
|
||||||
// GC performs a garbage collection cycle.
|
|
||||||
func GC() {
|
|
||||||
if gcDebug {
|
|
||||||
println("\nrunning collection cycle...")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Mark phase: mark all reachable objects, recursively.
|
|
||||||
markGlobals()
|
|
||||||
markRoots(getCurrentStackPointer(), stackTop) // assume a descending stack
|
|
||||||
|
|
||||||
// Sweep phase: free all non-marked objects and unmark marked objects for
|
|
||||||
// the next collection cycle.
|
|
||||||
sweep()
|
|
||||||
|
|
||||||
// Show how much has been sweeped, for debugging.
|
|
||||||
if gcDebug {
|
|
||||||
dumpHeap()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//go:nobounds
|
|
||||||
func markGlobals() {
|
|
||||||
for i := uintptr(0); i < trackedGlobalsLength; i++ {
|
|
||||||
if trackedGlobalsBitmap[i/8]&(1<<(i%8)) != 0 {
|
|
||||||
addr := trackedGlobalsStart + i*unsafe.Alignof(uintptr(0))
|
|
||||||
root := *(*uintptr)(unsafe.Pointer(addr))
|
|
||||||
markRoot(addr, root)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// markRoots reads all pointers from start to end (exclusive) and if they look
|
|
||||||
// like a heap pointer and are unmarked, marks them and scans that object as
|
|
||||||
// well (recursively). The start and end parameters must be valid pointers and
|
|
||||||
// must be aligned.
|
|
||||||
func markRoots(start, end uintptr) {
|
|
||||||
if gcDebug {
|
|
||||||
println("mark from", start, "to", end, int(end-start))
|
|
||||||
}
|
|
||||||
|
|
||||||
for addr := start; addr != end; addr += unsafe.Sizeof(addr) {
|
|
||||||
root := *(*uintptr)(unsafe.Pointer(addr))
|
|
||||||
markRoot(addr, root)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func markRoot(addr, root uintptr) {
|
|
||||||
if addressOnHeap(root) {
|
|
||||||
block := blockFromAddr(root)
|
|
||||||
head := block.findHead()
|
|
||||||
if head.state() != blockStateMark {
|
|
||||||
if gcDebug {
|
|
||||||
println("found unmarked pointer", root, "at address", addr)
|
|
||||||
}
|
|
||||||
head.setState(blockStateMark)
|
|
||||||
next := block.findNext()
|
|
||||||
// TODO: avoid recursion as much as possible
|
|
||||||
markRoots(head.address(), next.address())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
+15
-49
@@ -60,20 +60,14 @@ func hashmapTopHash(hash uint32) uint8 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Create a new hashmap with the given keySize and valueSize.
|
// Create a new hashmap with the given keySize and valueSize.
|
||||||
func hashmapMake(keySize, valueSize uint8, sizeHint uintptr) *hashmap {
|
func hashmapMake(keySize, valueSize uint8) *hashmap {
|
||||||
numBuckets := sizeHint / 8
|
|
||||||
bucketBits := uint8(0)
|
|
||||||
for numBuckets != 0 {
|
|
||||||
numBuckets /= 2
|
|
||||||
bucketBits++
|
|
||||||
}
|
|
||||||
bucketBufSize := unsafe.Sizeof(hashmapBucket{}) + uintptr(keySize)*8 + uintptr(valueSize)*8
|
bucketBufSize := unsafe.Sizeof(hashmapBucket{}) + uintptr(keySize)*8 + uintptr(valueSize)*8
|
||||||
buckets := alloc(bucketBufSize * (1 << bucketBits))
|
bucket := alloc(bucketBufSize)
|
||||||
return &hashmap{
|
return &hashmap{
|
||||||
buckets: buckets,
|
buckets: bucket,
|
||||||
keySize: keySize,
|
keySize: keySize,
|
||||||
valueSize: valueSize,
|
valueSize: valueSize,
|
||||||
bucketBits: bucketBits,
|
bucketBits: 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -89,20 +83,13 @@ func hashmapLen(m *hashmap) int {
|
|||||||
// Set a specified key to a given value. Grow the map if necessary.
|
// Set a specified key to a given value. Grow the map if necessary.
|
||||||
//go:nobounds
|
//go:nobounds
|
||||||
func hashmapSet(m *hashmap, key unsafe.Pointer, value unsafe.Pointer, hash uint32, keyEqual func(x, y unsafe.Pointer, n uintptr) bool) {
|
func hashmapSet(m *hashmap, key unsafe.Pointer, value unsafe.Pointer, hash uint32, keyEqual func(x, y unsafe.Pointer, n uintptr) bool) {
|
||||||
tophash := hashmapTopHash(hash)
|
|
||||||
|
|
||||||
if m.buckets == nil {
|
|
||||||
// No bucket was allocated yet, do so now.
|
|
||||||
m.buckets = unsafe.Pointer(hashmapInsertIntoNewBucket(m, key, value, tophash))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
numBuckets := uintptr(1) << m.bucketBits
|
numBuckets := uintptr(1) << m.bucketBits
|
||||||
bucketNumber := (uintptr(hash) & (numBuckets - 1))
|
bucketNumber := (uintptr(hash) & (numBuckets - 1))
|
||||||
bucketSize := unsafe.Sizeof(hashmapBucket{}) + uintptr(m.keySize)*8 + uintptr(m.valueSize)*8
|
bucketSize := unsafe.Sizeof(hashmapBucket{}) + uintptr(m.keySize)*8 + uintptr(m.valueSize)*8
|
||||||
bucketAddr := uintptr(m.buckets) + bucketSize*bucketNumber
|
bucketAddr := uintptr(m.buckets) + bucketSize*bucketNumber
|
||||||
bucket := (*hashmapBucket)(unsafe.Pointer(bucketAddr))
|
bucket := (*hashmapBucket)(unsafe.Pointer(bucketAddr))
|
||||||
var lastBucket *hashmapBucket
|
|
||||||
|
tophash := hashmapTopHash(hash)
|
||||||
|
|
||||||
// See whether the key already exists somewhere.
|
// See whether the key already exists somewhere.
|
||||||
var emptySlotKey unsafe.Pointer
|
var emptySlotKey unsafe.Pointer
|
||||||
@@ -111,9 +98,9 @@ func hashmapSet(m *hashmap, key unsafe.Pointer, value unsafe.Pointer, hash uint3
|
|||||||
for bucket != nil {
|
for bucket != nil {
|
||||||
for i := uintptr(0); i < 8; i++ {
|
for i := uintptr(0); i < 8; i++ {
|
||||||
slotKeyOffset := unsafe.Sizeof(hashmapBucket{}) + uintptr(m.keySize)*uintptr(i)
|
slotKeyOffset := unsafe.Sizeof(hashmapBucket{}) + uintptr(m.keySize)*uintptr(i)
|
||||||
slotKey := unsafe.Pointer(uintptr(unsafe.Pointer(bucket)) + slotKeyOffset)
|
slotKey := unsafe.Pointer(bucketAddr + slotKeyOffset)
|
||||||
slotValueOffset := unsafe.Sizeof(hashmapBucket{}) + uintptr(m.keySize)*8 + uintptr(m.valueSize)*uintptr(i)
|
slotValueOffset := unsafe.Sizeof(hashmapBucket{}) + uintptr(m.keySize)*8 + uintptr(m.valueSize)*uintptr(i)
|
||||||
slotValue := unsafe.Pointer(uintptr(unsafe.Pointer(bucket)) + slotValueOffset)
|
slotValue := unsafe.Pointer(bucketAddr + slotValueOffset)
|
||||||
if bucket.tophash[i] == 0 && emptySlotKey == nil {
|
if bucket.tophash[i] == 0 && emptySlotKey == nil {
|
||||||
// Found an empty slot, store it for if we couldn't find an
|
// Found an empty slot, store it for if we couldn't find an
|
||||||
// existing slot.
|
// existing slot.
|
||||||
@@ -122,7 +109,7 @@ func hashmapSet(m *hashmap, key unsafe.Pointer, value unsafe.Pointer, hash uint3
|
|||||||
emptySlotTophash = &bucket.tophash[i]
|
emptySlotTophash = &bucket.tophash[i]
|
||||||
}
|
}
|
||||||
if bucket.tophash[i] == tophash {
|
if bucket.tophash[i] == tophash {
|
||||||
// Could be an existing key that's the same.
|
// Could be an existing value that's the same.
|
||||||
if keyEqual(key, slotKey, uintptr(m.keySize)) {
|
if keyEqual(key, slotKey, uintptr(m.keySize)) {
|
||||||
// found same key, replace it
|
// found same key, replace it
|
||||||
memcpy(slotValue, value, uintptr(m.valueSize))
|
memcpy(slotValue, value, uintptr(m.valueSize))
|
||||||
@@ -130,37 +117,16 @@ func hashmapSet(m *hashmap, key unsafe.Pointer, value unsafe.Pointer, hash uint3
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
lastBucket = bucket
|
|
||||||
bucket = bucket.next
|
bucket = bucket.next
|
||||||
}
|
}
|
||||||
if emptySlotKey == nil {
|
if emptySlotKey != nil {
|
||||||
// Add a new bucket to the bucket chain.
|
m.count++
|
||||||
// TODO: rebalance if necessary to avoid O(n) insert and lookup time.
|
memcpy(emptySlotKey, key, uintptr(m.keySize))
|
||||||
lastBucket.next = (*hashmapBucket)(hashmapInsertIntoNewBucket(m, key, value, tophash))
|
memcpy(emptySlotValue, value, uintptr(m.valueSize))
|
||||||
|
*emptySlotTophash = tophash
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
m.count++
|
panic("todo: hashmap: grow bucket")
|
||||||
memcpy(emptySlotKey, key, uintptr(m.keySize))
|
|
||||||
memcpy(emptySlotValue, value, uintptr(m.valueSize))
|
|
||||||
*emptySlotTophash = tophash
|
|
||||||
}
|
|
||||||
|
|
||||||
// hashmapInsertIntoNewBucket creates a new bucket, inserts the given key and
|
|
||||||
// value into the bucket, and returns a pointer to this bucket.
|
|
||||||
func hashmapInsertIntoNewBucket(m *hashmap, key, value unsafe.Pointer, tophash uint8) *hashmapBucket {
|
|
||||||
bucketBufSize := unsafe.Sizeof(hashmapBucket{}) + uintptr(m.keySize)*8 + uintptr(m.valueSize)*8
|
|
||||||
bucketBuf := alloc(bucketBufSize)
|
|
||||||
// Insert into the first slot, which is empty as it has just been allocated.
|
|
||||||
slotKeyOffset := unsafe.Sizeof(hashmapBucket{})
|
|
||||||
slotKey := unsafe.Pointer(uintptr(bucketBuf) + slotKeyOffset)
|
|
||||||
slotValueOffset := unsafe.Sizeof(hashmapBucket{}) + uintptr(m.keySize)*8
|
|
||||||
slotValue := unsafe.Pointer(uintptr(bucketBuf) + slotValueOffset)
|
|
||||||
m.count++
|
|
||||||
memcpy(slotKey, key, uintptr(m.keySize))
|
|
||||||
memcpy(slotValue, value, uintptr(m.valueSize))
|
|
||||||
bucket := (*hashmapBucket)(bucketBuf)
|
|
||||||
bucket.tophash[0] = tophash
|
|
||||||
return bucket
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the value of a specified key, or zero the value if not found.
|
// Get the value of a specified key, or zero the value if not found.
|
||||||
|
|||||||
@@ -17,19 +17,19 @@ func sleepWDT(period uint8) {
|
|||||||
avr.Asm("cli")
|
avr.Asm("cli")
|
||||||
avr.Asm("wdr")
|
avr.Asm("wdr")
|
||||||
// Start timed sequence.
|
// Start timed sequence.
|
||||||
avr.WDTCSR.SetBits(avr.WDTCSR_WDCE | avr.WDTCSR_WDE)
|
*avr.WDTCSR |= avr.WDTCSR_WDCE | avr.WDTCSR_WDE
|
||||||
// Enable WDT and set new timeout
|
// Enable WDT and set new timeout
|
||||||
avr.WDTCSR.SetBits(avr.WDTCSR_WDIE | period)
|
*avr.WDTCSR = avr.WDTCSR_WDIE | avr.RegValue(period)
|
||||||
avr.Asm("sei")
|
avr.Asm("sei")
|
||||||
|
|
||||||
// Set sleep mode to idle and enable sleep mode.
|
// Set sleep mode to idle and enable sleep mode.
|
||||||
// Note: when using something other than idle, the UART won't work
|
// Note: when using something other than idle, the UART won't work
|
||||||
// correctly. This needs to be fixed, though, so we can truly sleep.
|
// correctly. This needs to be fixed, though, so we can truly sleep.
|
||||||
avr.SMCR.Set((0 << 1) | avr.SMCR_SE)
|
*avr.SMCR = (0 << 1) | avr.SMCR_SE
|
||||||
|
|
||||||
// go to sleep
|
// go to sleep
|
||||||
avr.Asm("sleep")
|
avr.Asm("sleep")
|
||||||
|
|
||||||
// disable sleep
|
// disable sleep
|
||||||
avr.SMCR.Set(0)
|
*avr.SMCR = 0
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,34 +0,0 @@
|
|||||||
// Package volatile provides definitions for volatile loads and stores. These
|
|
||||||
// are implemented as compiler builtins.
|
|
||||||
//
|
|
||||||
// The load operations load a volatile value. The store operations store to a
|
|
||||||
// volatile value. The compiler will emit exactly one load or store operation
|
|
||||||
// when possible and will not reorder volatile operations. However, the compiler
|
|
||||||
// may move other operations across load/store operations, so make sure that all
|
|
||||||
// relevant loads/stores are done in a volatile way if this is a problem.
|
|
||||||
//
|
|
||||||
// These loads and stores are commonly used to read/write values from memory
|
|
||||||
// mapped peripheral devices. They do not provide atomicity, use the sync/atomic
|
|
||||||
// package for that.
|
|
||||||
//
|
|
||||||
// For more details: https://llvm.org/docs/LangRef.html#volatile-memory-accesses
|
|
||||||
// and https://blog.regehr.org/archives/28.
|
|
||||||
package volatile
|
|
||||||
|
|
||||||
// LoadUint8 loads the volatile value *addr.
|
|
||||||
func LoadUint8(addr *uint8) (val uint8)
|
|
||||||
|
|
||||||
// LoadUint16 loads the volatile value *addr.
|
|
||||||
func LoadUint16(addr *uint16) (val uint16)
|
|
||||||
|
|
||||||
// LoadUint32 loads the volatile value *addr.
|
|
||||||
func LoadUint32(addr *uint32) (val uint32)
|
|
||||||
|
|
||||||
// StoreUint8 stores val to the volatile value *addr.
|
|
||||||
func StoreUint8(addr *uint8, val uint8)
|
|
||||||
|
|
||||||
// StoreUint16 stores val to the volatile value *addr.
|
|
||||||
func StoreUint16(addr *uint16, val uint16)
|
|
||||||
|
|
||||||
// StoreUint32 stores val to the volatile value *addr.
|
|
||||||
func StoreUint32(addr *uint32, val uint32)
|
|
||||||
@@ -3,7 +3,7 @@
|
|||||||
"goos": "linux",
|
"goos": "linux",
|
||||||
"goarch": "arm",
|
"goarch": "arm",
|
||||||
"compiler": "clang",
|
"compiler": "clang",
|
||||||
"gc": "precise",
|
"gc": "marksweep",
|
||||||
"linker": "ld.lld",
|
"linker": "ld.lld",
|
||||||
"rtlib": "compiler-rt",
|
"rtlib": "compiler-rt",
|
||||||
"cflags": [
|
"cflags": [
|
||||||
|
|||||||
Vendored
-22
@@ -4,22 +4,6 @@ type Thing struct {
|
|||||||
name string
|
name string
|
||||||
}
|
}
|
||||||
|
|
||||||
type ThingOption func(*Thing)
|
|
||||||
|
|
||||||
func WithName(name string) ThingOption {
|
|
||||||
return func(t *Thing) {
|
|
||||||
t.name = name
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewThing(opts ...ThingOption) *Thing {
|
|
||||||
t := &Thing{}
|
|
||||||
for _, opt := range opts {
|
|
||||||
opt(t)
|
|
||||||
}
|
|
||||||
return t
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t Thing) String() string {
|
func (t Thing) String() string {
|
||||||
return t.name
|
return t.name
|
||||||
}
|
}
|
||||||
@@ -52,12 +36,6 @@ func main() {
|
|||||||
runFunc(func(i int) {
|
runFunc(func(i int) {
|
||||||
println("inside fp closure:", thing.String(), i)
|
println("inside fp closure:", thing.String(), i)
|
||||||
}, 3)
|
}, 3)
|
||||||
|
|
||||||
// functional arguments
|
|
||||||
thingFunctionalArgs1 := NewThing()
|
|
||||||
thingFunctionalArgs1.Print("functional args 1")
|
|
||||||
thingFunctionalArgs2 := NewThing(WithName("named thing"))
|
|
||||||
thingFunctionalArgs2.Print("functional args 2")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func runFunc(f func(int), arg int) {
|
func runFunc(f func(int), arg int) {
|
||||||
|
|||||||
Vendored
-2
@@ -7,5 +7,3 @@ Thing.Print: foo arg: bar
|
|||||||
bound method: foo
|
bound method: foo
|
||||||
thing inside closure: foo
|
thing inside closure: foo
|
||||||
inside fp closure: foo 3
|
inside fp closure: foo 3
|
||||||
Thing.Print: arg: functional args 1
|
|
||||||
Thing.Print: named thing arg: functional args 2
|
|
||||||
|
|||||||
Vendored
-6
@@ -1,6 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
// Make sure CGo supports multiple files.
|
|
||||||
|
|
||||||
// int fortytwo(void);
|
|
||||||
import "C"
|
|
||||||
Vendored
-6
@@ -7,8 +7,6 @@ int mul(int, int);
|
|||||||
*/
|
*/
|
||||||
import "C"
|
import "C"
|
||||||
|
|
||||||
import "C"
|
|
||||||
|
|
||||||
import "unsafe"
|
import "unsafe"
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
@@ -20,10 +18,6 @@ func main() {
|
|||||||
var y C.longlong = -(1 << 40)
|
var y C.longlong = -(1 << 40)
|
||||||
println("longlong:", y)
|
println("longlong:", y)
|
||||||
println("global:", C.global)
|
println("global:", C.global)
|
||||||
println("defined ints:", C.CONST_INT, C.CONST_INT2)
|
|
||||||
println("defined floats:", C.CONST_FLOAT, C.CONST_FLOAT2)
|
|
||||||
println("defined string:", C.CONST_STRING)
|
|
||||||
println("defined char:", C.CONST_CHAR)
|
|
||||||
var ptr C.intPointer
|
var ptr C.intPointer
|
||||||
var n C.int = 15
|
var n C.int = 15
|
||||||
ptr = C.intPointer(&n)
|
ptr = C.intPointer(&n)
|
||||||
|
|||||||
Vendored
-7
@@ -10,13 +10,6 @@ int doCallback(int a, int b, binop_t cb);
|
|||||||
typedef int * intPointer;
|
typedef int * intPointer;
|
||||||
void store(int value, int *ptr);
|
void store(int value, int *ptr);
|
||||||
|
|
||||||
# define CONST_INT 5
|
|
||||||
# define CONST_INT2 5llu
|
|
||||||
# define CONST_FLOAT 5.8
|
|
||||||
# define CONST_FLOAT2 5.8f
|
|
||||||
# define CONST_CHAR 'c'
|
|
||||||
# define CONST_STRING "defined string"
|
|
||||||
|
|
||||||
// this signature should not be included by CGo
|
// this signature should not be included by CGo
|
||||||
void unusedFunction2(int x, __builtin_va_list args);
|
void unusedFunction2(int x, __builtin_va_list args);
|
||||||
|
|
||||||
|
|||||||
Vendored
-4
@@ -4,10 +4,6 @@ myint: 3 5
|
|||||||
myint size: 2
|
myint size: 2
|
||||||
longlong: -1099511627776
|
longlong: -1099511627776
|
||||||
global: 3
|
global: 3
|
||||||
defined ints: 5 5
|
|
||||||
defined floats: +5.800000e+000 +5.800000e+000
|
|
||||||
defined string: defined string
|
|
||||||
defined char: 99
|
|
||||||
15: 15
|
15: 15
|
||||||
25: 25
|
25: 25
|
||||||
callback 1: 50
|
callback 1: 50
|
||||||
|
|||||||
Vendored
+1
-1
@@ -4,7 +4,7 @@ import "time"
|
|||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
ch := make(chan int)
|
ch := make(chan int)
|
||||||
println("len, cap of channel:", len(ch), cap(ch), ch == nil)
|
println("len, cap of channel:", len(ch), cap(ch))
|
||||||
go sender(ch)
|
go sender(ch)
|
||||||
|
|
||||||
n, ok := <-ch
|
n, ok := <-ch
|
||||||
|
|||||||
Vendored
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
len, cap of channel: 0 0 false
|
len, cap of channel: 0 0
|
||||||
recv from open channel: 1 true
|
recv from open channel: 1 true
|
||||||
received num: 2
|
received num: 2
|
||||||
received num: 3
|
received num: 3
|
||||||
|
|||||||
Vendored
-32
@@ -47,18 +47,6 @@ func main() {
|
|||||||
println(testMapArrayKey[arrKey])
|
println(testMapArrayKey[arrKey])
|
||||||
testMapArrayKey[arrKey] = 5555
|
testMapArrayKey[arrKey] = 5555
|
||||||
println(testMapArrayKey[arrKey])
|
println(testMapArrayKey[arrKey])
|
||||||
|
|
||||||
// test preallocated map
|
|
||||||
squares := make(map[int]int, 200)
|
|
||||||
testBigMap(squares, 100)
|
|
||||||
println("tested preallocated map")
|
|
||||||
|
|
||||||
// test growing maps
|
|
||||||
squares = make(map[int]int, 0)
|
|
||||||
testBigMap(squares, 10)
|
|
||||||
squares = make(map[int]int, 20)
|
|
||||||
testBigMap(squares, 40)
|
|
||||||
println("tested growing of a map")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func readMap(m map[string]int, key string) {
|
func readMap(m map[string]int, key string) {
|
||||||
@@ -68,27 +56,7 @@ func readMap(m map[string]int, key string) {
|
|||||||
println(" ", k, "=", v)
|
println(" ", k, "=", v)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func lookup(m map[string]int, key string) {
|
func lookup(m map[string]int, key string) {
|
||||||
value, ok := m[key]
|
value, ok := m[key]
|
||||||
println("lookup with comma-ok:", key, value, ok)
|
println("lookup with comma-ok:", key, value, ok)
|
||||||
}
|
}
|
||||||
|
|
||||||
func testBigMap(squares map[int]int, n int) {
|
|
||||||
for i := 0; i < n; i++ {
|
|
||||||
if len(squares) != i {
|
|
||||||
println("unexpected length:", len(squares), "at i =", i)
|
|
||||||
}
|
|
||||||
squares[i] = i*i
|
|
||||||
for j := 0; j <= i; j++ {
|
|
||||||
if v, ok := squares[j]; !ok || v != j*j {
|
|
||||||
if !ok {
|
|
||||||
println("key not found in squares map:", j)
|
|
||||||
} else {
|
|
||||||
println("unexpected value read back from squares map:", j, v)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
Vendored
-2
@@ -54,5 +54,3 @@ true false 0
|
|||||||
42
|
42
|
||||||
4321
|
4321
|
||||||
5555
|
5555
|
||||||
tested preallocated map
|
|
||||||
tested growing of a map
|
|
||||||
|
|||||||
+4
-46
@@ -151,54 +151,12 @@ def writeGo(outdir, device):
|
|||||||
// {description}
|
// {description}
|
||||||
package {pkgName}
|
package {pkgName}
|
||||||
|
|
||||||
import (
|
import "unsafe"
|
||||||
"runtime/volatile"
|
|
||||||
"unsafe"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Special type that causes loads/stores to be volatile (necessary for
|
// Special type that causes loads/stores to be volatile (necessary for
|
||||||
// memory-mapped registers).
|
// memory-mapped registers).
|
||||||
type Register8 struct {{
|
//go:volatile
|
||||||
Reg uint8
|
type RegValue uint8
|
||||||
}}
|
|
||||||
|
|
||||||
// Get returns the value in the register. It is the volatile equivalent of:
|
|
||||||
//
|
|
||||||
// *r.Reg
|
|
||||||
//
|
|
||||||
//go:inline
|
|
||||||
func (r *Register8) Get() uint8 {{
|
|
||||||
return volatile.LoadUint8(&r.Reg)
|
|
||||||
}}
|
|
||||||
|
|
||||||
// Set updates the register value. It is the volatile equivalent of:
|
|
||||||
//
|
|
||||||
// *r.Reg = value
|
|
||||||
//
|
|
||||||
//go:inline
|
|
||||||
func (r *Register8) Set(value uint8) {{
|
|
||||||
volatile.StoreUint8(&r.Reg, value)
|
|
||||||
}}
|
|
||||||
|
|
||||||
// SetBits reads the register, sets the given bits, and writes it back. It is
|
|
||||||
// the volatile equivalent of:
|
|
||||||
//
|
|
||||||
// r.Reg |= value
|
|
||||||
//
|
|
||||||
//go:inline
|
|
||||||
func (r *Register8) SetBits(value uint8) {{
|
|
||||||
volatile.StoreUint8(&r.Reg, volatile.LoadUint8(&r.Reg) | value)
|
|
||||||
}}
|
|
||||||
|
|
||||||
// ClearBits reads the register, clears the given bits, and writes it back. It
|
|
||||||
// is the volatile equivalent of:
|
|
||||||
//
|
|
||||||
// r.Reg &^= value
|
|
||||||
//
|
|
||||||
//go:inline
|
|
||||||
func (r *Register8) ClearBits(value uint8) {{
|
|
||||||
volatile.StoreUint8(&r.Reg, volatile.LoadUint8(&r.Reg) &^ value)
|
|
||||||
}}
|
|
||||||
|
|
||||||
// Some information about this device.
|
// Some information about this device.
|
||||||
const (
|
const (
|
||||||
@@ -221,7 +179,7 @@ const (
|
|||||||
out.write('\n\t// {description}\n'.format(**peripheral))
|
out.write('\n\t// {description}\n'.format(**peripheral))
|
||||||
for register in peripheral['registers']:
|
for register in peripheral['registers']:
|
||||||
for variant in register['variants']:
|
for variant in register['variants']:
|
||||||
out.write('\t{name} = (*Register8)(unsafe.Pointer(uintptr(0x{address:x})))\n'.format(**variant))
|
out.write('\t{name} = (*RegValue)(unsafe.Pointer(uintptr(0x{address:x})))\n'.format(**variant))
|
||||||
out.write(')\n')
|
out.write(')\n')
|
||||||
|
|
||||||
for peripheral in device.peripherals:
|
for peripheral in device.peripherals:
|
||||||
|
|||||||
Reference in New Issue
Block a user