Compare commits

..

1 Commits

Author SHA1 Message Date
Ayke van Laethem 71fa131ef9 machine/dummy: add hooks to override peripheral methods 2019-05-12 07:36:46 +02:00
54 changed files with 595 additions and 1783 deletions
-1
View File
@@ -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:
+1 -1
View File
@@ -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:
-40
View File
@@ -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
View File
@@ -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
-104
View File
@@ -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
View File
@@ -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,
-30
View File
@@ -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
View File
@@ -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())
-8
View File
@@ -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
} }
-26
View File
@@ -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
}
+3 -3
View File
@@ -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.
-2
View File
@@ -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.
+3
View File
@@ -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 {
-2
View File
@@ -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)
+6 -29
View File
@@ -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
View File
@@ -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{
+57 -145
View File
@@ -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
View File
@@ -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 {
+6 -6
View File
@@ -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)
+1 -1
View File
@@ -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")
-3
View File
@@ -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)
}) })
-25
View File
@@ -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], "...")
}
}
-12
View File
@@ -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}
)
-12
View File
@@ -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}
)
-54
View File
@@ -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
}
+50 -50
View File
@@ -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))
} }
-229
View File
@@ -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
+4 -4
View File
@@ -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
View File
@@ -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.
+60 -12
View File
@@ -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
}
+19 -8
View File
@@ -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
View File
@@ -1,4 +1,4 @@
// +build nrf stm32f103xx atsamd21g18a // +build js,wasm nrf stm32f103xx atsamd21g18a
package machine package machine
-91
View File
@@ -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
View File
@@ -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.
}
-116
View File
@@ -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
View File
@@ -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.
+4 -4
View File
@@ -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
} }
-34
View File
@@ -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)
+1 -1
View File
@@ -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": [
-22
View File
@@ -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) {
-2
View File
@@ -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
-6
View File
@@ -1,6 +0,0 @@
package main
// Make sure CGo supports multiple files.
// int fortytwo(void);
import "C"
-6
View File
@@ -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)
-7
View File
@@ -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);
-4
View File
@@ -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
+1 -1
View File
@@ -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
+1 -1
View File
@@ -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
-32
View File
@@ -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
}
}
}
}
-2
View File
@@ -54,5 +54,3 @@ true false 0
42 42
4321 4321
5555 5555
tested preallocated map
tested growing of a map
+4 -46
View File
@@ -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: