Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Extend string support: concatenation, len, (fixed) println.

To implement string concatenaton in a sane way, a runtime
library is required. To do this, I've also had to implement
support for unsafe.Pointer.

I intend to write as much of the runtime library in Go as
possible. To support the Go-based runtime library, there
will be various "intrinsics" written directly in LLVM
bitcode (e.g. memory management, threading).
  • Loading branch information...
commit 54e1c1014141e69d6566e2aa7de5156172a79cee 1 parent e36845e
Andrew Wilkins authored
3  compiler.go
View
@@ -215,6 +215,9 @@ func Compile(fset *token.FileSet, pkg *ast.Package) (m *Module, err error) {
}
}
+ // Define intrinsics for use by the runtime: malloc, free, memcpy, etc.
+ compiler.defineRuntimeIntrinsics()
+
// Create global constructors.
//
// XXX When imports are handled, we'll need to defer creating
3  decl.go
View
@@ -92,6 +92,9 @@ func (c *compiler) VisitFuncDecl(f *ast.FuncDecl) Value {
if fn == nil {
fn = c.VisitFuncProtoDecl(f)
}
+ if f.Body == nil {
+ return fn
+ }
fn_type := fn.Type().(*types.Func)
llvm_fn := fn.LLVMValue()
4 expr.go
View
@@ -225,6 +225,10 @@ func (c *compiler) VisitSelectorExpr(expr *ast.SelectorExpr) Value {
pkgident := (expr.X).(*ast.Ident)
pkgscope := (pkgident.Obj.Data).(*ast.Scope)
obj := pkgscope.Lookup(expr.Sel.String())
+ if obj == nil {
+ panic(fmt.Errorf("Failed to locate object: %v.%v",
+ pkgident.Name, expr.Sel.String()))
+ }
if obj.Kind == ast.Typ {
return TypeValue{obj.Type.(types.Type)}
}
76 intrinsics.go
View
@@ -0,0 +1,76 @@
+/*
+Copyright (c) 2011, 2012 Andrew Wilkins <axwalk@gmail.com>
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+of the Software, and to permit persons to whom the Software is furnished to do
+so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+*/
+
+package llgo
+
+import (
+ "github.com/axw/gollvm/llvm"
+)
+
+func (c *compiler) defineRuntimeIntrinsics() {
+ fn := c.module.NamedFunction("runtime.malloc")
+ if !fn.IsNil() {c.defineMallocFunction(fn)}
+
+ fn = c.module.NamedFunction("runtime.memcpy")
+ if !fn.IsNil() {c.defineMemcpyFunction(fn)}
+}
+
+func (c *compiler) defineMallocFunction(fn llvm.Value) {
+ entry := llvm.AddBasicBlock(fn, "entry")
+ c.builder.SetInsertPointAtEnd(entry)
+ size := fn.FirstParam()
+ ptr := c.builder.CreateArrayMalloc(llvm.Int8Type(), size, "")
+ // XXX memset to zero, or leave that to Go runtime code?
+ fn_type := fn.Type().ElementType()
+ result := c.builder.CreatePtrToInt(ptr, fn_type.ReturnType(), "")
+ c.builder.CreateRet(result)
+}
+
+func (c *compiler) defineMemcpyFunction(fn llvm.Value) {
+ entry := llvm.AddBasicBlock(fn, "entry")
+ c.builder.SetInsertPointAtEnd(entry)
+ dst, src, size := fn.Param(0), fn.Param(1), fn.Param(2)
+
+ pint8 := llvm.PointerType(llvm.Int8Type(), 0)
+ dst = c.builder.CreateIntToPtr(dst, pint8, "")
+ src = c.builder.CreateIntToPtr(src, pint8, "")
+
+ memcpyName := "llvm.memcpy.p0i8.p0i8.i32"
+ memcpy := c.module.NamedFunction(memcpyName)
+ if memcpy.IsNil() {
+ paramtypes := []llvm.Type{
+ pint8, pint8, llvm.Int32Type(), llvm.Int32Type(), llvm.Int1Type()}
+ memcpyType := llvm.FunctionType(llvm.VoidType(), paramtypes, false)
+ memcpy = llvm.AddFunction(c.module.Module, memcpyName, memcpyType)
+ }
+
+ args := []llvm.Value{
+ dst, src, size,
+ llvm.ConstInt(llvm.Int32Type(), 1, false), // single byte alignment
+ llvm.ConstInt(llvm.Int1Type(), 0, false), // not volatile
+ }
+ c.builder.CreateCall(memcpy, args, "")
+ c.builder.CreateRetVoid()
+}
+
+// vim: set ft=go:
+
7 len.go
View
@@ -81,6 +81,13 @@ func (c *compiler) VisitLen(expr *ast.CallExpr) Value {
//return c.NewConstValue(token.INT, string(sz.ZExtValue()))
return c.NewLLVMValue(sz, types.Int32)
+ case *types.Basic:
+ if typ == types.String.Underlying {
+ ptr := value.(*LLVMValue).address
+ len_field := c.builder.CreateStructGEP(ptr.LLVMValue(), 1, "")
+ len_value := c.builder.CreateLoad(len_field, "")
+ return c.NewLLVMValue(len_value, types.Int32)
+ }
}
panic(fmt.Sprint("Unhandled value type: ", value.Type()))
}
10 llgo/testdata/strings/add.go
View
@@ -0,0 +1,10 @@
+package main
+
+func main() {
+ a := "abc"
+ b := "123"
+ c := a + b
+ println(len(a), len(b), len(c))
+ println(c)
+}
+
0  llgo/testdata/unsafe.go → llgo/testdata/unsafe/pointer.go
View
File renamed without changes
2  llgo/unsafe_test.go
View
@@ -5,7 +5,7 @@ import (
)
func TestUnsafePointer(t *testing.T) {
- err := runAndCheckMain(testdata("unsafe.go"), checkStringsEqual)
+ err := runAndCheckMain(testdata("unsafe/pointer.go"), checkStringsEqual)
if err != nil {
t.Fatal(err)
}
2  llvmtypes.go
View
@@ -92,7 +92,7 @@ func (tm *TypeMap) basicLLVMType(b *types.Basic) llvm.Type {
return llvm.Int8Type()
case types.Int16Kind, types.Uint16Kind:
return llvm.Int16Type()
- case types.UintptrKind, types.Int32Kind, types.Uint32Kind: // XXX uintptr size depends on bit width
+ case types.UnsafePointerKind, types.UintptrKind, types.Int32Kind, types.Uint32Kind: // XXX uintptr size depends on bit width
return llvm.Int32Type()
case types.Int64Kind, types.Uint64Kind:
return llvm.Int64Type()
3  println.go
View
@@ -86,7 +86,8 @@ func (c *compiler) VisitPrintln(expr *ast.CallExpr) Value {
lenval := c.builder.CreateExtractValue(llvm_value, 1, "")
llvm_value = ptrval
args = append(args, lenval)
- format += "%*s"
+ format += "%.*s"
+
case types.BoolKind:
format += "%d"
llvm_value = c.builder.CreateZExt(llvm_value, llvm.Int32Type(), "")
57 runtime/strings.go
View
@@ -0,0 +1,57 @@
+/*
+Copyright (c) 2011, 2012 Andrew Wilkins <axwalk@gmail.com>
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+of the Software, and to permit persons to whom the Software is furnished to do
+so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+*/
+
+package runtime
+
+import "unsafe"
+
+type str struct {
+ ptr *uint8
+ size int
+}
+
+func malloc(int) unsafe.Pointer
+func memcpy(dst, src unsafe.Pointer, size int)
+
+func strcat(a, b str) str {
+ if a.size == 0 {
+ return b
+ } else if b.size == 0 {
+ return a
+ }
+
+ mem := malloc(a.size + b.size)
+ if mem == unsafe.Pointer(0) {
+ // TODO panic? abort?
+ }
+
+ memcpy(mem, unsafe.Pointer(a.ptr), a.size)
+ memcpy(unsafe.Pointer(uintptr(mem) + uintptr(a.size)),
+ unsafe.Pointer(b.ptr), b.size)
+
+ a.ptr = (*uint8)(mem)
+ a.size = a.size + b.size
+ return a
+}
+
+// vim: set ft=go:
+
22 stmt.go
View
@@ -141,12 +141,16 @@ func (c *compiler) VisitAssignStmt(stmt *ast.AssignStmt) {
func (c *compiler) VisitIfStmt(stmt *ast.IfStmt) {
curr_block := c.builder.GetInsertBlock()
- if_block := llvm.AddBasicBlock(curr_block.Parent(), "if")
- var else_block llvm.BasicBlock
+ resume_block := llvm.AddBasicBlock(curr_block.Parent(), "endif")
+ resume_block.MoveAfter(curr_block)
+
+ var if_block, else_block llvm.BasicBlock
if stmt.Else != nil {
- else_block = llvm.AddBasicBlock(curr_block.Parent(), "else")
+ else_block = llvm.InsertBasicBlock(resume_block, "else")
+ if_block = llvm.InsertBasicBlock(else_block, "if")
+ } else {
+ if_block = llvm.InsertBasicBlock(resume_block, "if")
}
- resume_block := llvm.AddBasicBlock(curr_block.Parent(), "endif")
if stmt.Else == nil {
else_block = resume_block
}
@@ -164,9 +168,7 @@ func (c *compiler) VisitIfStmt(stmt *ast.IfStmt) {
}
}
- c.builder.CreateCondBr(
- cond_val.LLVMValue(), if_block, else_block)
-
+ c.builder.CreateCondBr(cond_val.LLVMValue(), if_block, else_block)
c.builder.SetInsertPointAtEnd(if_block)
c.VisitBlockStmt(stmt.Body)
if in := if_block.LastInstruction(); in.IsNil() || in.IsATerminatorInst().IsNil() {
@@ -181,7 +183,13 @@ func (c *compiler) VisitIfStmt(stmt *ast.IfStmt) {
}
}
+ // If there's a block after the "resume" block (i.e. a nested control
+ // statement), then create a branch to it as the last instruction.
c.builder.SetInsertPointAtEnd(resume_block)
+ if last := resume_block.Parent().LastBasicBlock(); last != resume_block {
+ c.builder.CreateBr(last)
+ c.builder.SetInsertPointBefore(resume_block.FirstInstruction())
+ }
}
func (c *compiler) VisitForStmt(stmt *ast.ForStmt) {
8 strings.go
View
@@ -6,15 +6,15 @@ import (
)
func (c *compiler) concatenateStrings(lhs, rhs *LLVMValue) *LLVMValue {
- _llgo_strcat := c.module.Module.NamedFunction("_llgo_strcat")
- if _llgo_strcat.IsNil() {
+ strcat := c.module.Module.NamedFunction("runtime.strcat")
+ if strcat.IsNil() {
string_type := c.types.ToLLVM(types.String)
param_types := []llvm.Type{string_type, string_type}
func_type := llvm.FunctionType(string_type, param_types, false)
- _llgo_strcat = llvm.AddFunction(c.module.Module, "_llgo_strcat", func_type)
+ strcat = llvm.AddFunction(c.module.Module, "runtime.strcat", func_type)
}
args := []llvm.Value{lhs.LLVMValue(), rhs.LLVMValue()}
- result := c.builder.CreateCall(_llgo_strcat, args, "")
+ result := c.builder.CreateCall(strcat, args, "")
return c.NewLLVMValue(result, types.String)
}
28 types.go
View
@@ -27,7 +27,6 @@ import (
"github.com/axw/llgo/types"
"go/ast"
"math/big"
- "reflect"
"sort"
)
@@ -86,7 +85,8 @@ func (c *compiler) GetType(expr ast.Expr) types.Type {
case *ast.Ellipsis:
return c.GetType(x.Elt)
default:
- panic(fmt.Sprint("Unhandled Expr: ", reflect.TypeOf(x)))
+ value := c.VisitExpr(expr)
+ return value.(TypeValue).typ
}
return nil
}
@@ -101,18 +101,24 @@ func (c *compiler) VisitFuncType(f *ast.FuncType) *types.Func {
}
for i := 0; i < len(f.Params.List); i++ {
namecount := len(f.Params.List[i].Names)
- args := make([]*ast.Object, namecount)
typ := c.GetType(f.Params.List[i].Type)
- for j := 0; j < namecount; j++ {
- ident := f.Params.List[i].Names[j]
- if ident != nil {
- args[j] = ident.Obj
- } else {
- args[j] = ast.NewObj(ast.Var, "_")
+ if namecount == 0 {
+ arg := ast.NewObj(ast.Var, "_")
+ arg.Type = typ
+ fn_type.Params = append(fn_type.Params, arg)
+ } else {
+ args := make([]*ast.Object, namecount)
+ for j := 0; j < namecount; j++ {
+ ident := f.Params.List[i].Names[j]
+ if ident != nil {
+ args[j] = ident.Obj
+ } else {
+ args[j] = ast.NewObj(ast.Var, "_")
+ }
+ args[j].Type = typ
}
- args[j].Type = typ
+ fn_type.Params = append(fn_type.Params, args...)
}
- fn_type.Params = append(fn_type.Params, args...)
}
}
35 types/types.go
View
@@ -21,23 +21,24 @@ type BasicTypeKind reflect.Kind
// Constants for basic types.
const (
- BoolKind = BasicTypeKind(reflect.Bool)
- IntKind = BasicTypeKind(reflect.Int)
- Int8Kind = BasicTypeKind(reflect.Int8)
- Int16Kind = BasicTypeKind(reflect.Int16)
- Int32Kind = BasicTypeKind(reflect.Int32)
- Int64Kind = BasicTypeKind(reflect.Int64)
- UintKind = BasicTypeKind(reflect.Uint)
- Uint8Kind = BasicTypeKind(reflect.Uint8)
- Uint16Kind = BasicTypeKind(reflect.Uint16)
- Uint32Kind = BasicTypeKind(reflect.Uint32)
- Uint64Kind = BasicTypeKind(reflect.Uint64)
- UintptrKind = BasicTypeKind(reflect.Uintptr)
- Float32Kind = BasicTypeKind(reflect.Float32)
- Float64Kind = BasicTypeKind(reflect.Float64)
- Complex64Kind = BasicTypeKind(reflect.Complex64)
- Complex128Kind = BasicTypeKind(reflect.Complex128)
- StringKind = BasicTypeKind(reflect.String)
+ BoolKind = BasicTypeKind(reflect.Bool)
+ IntKind = BasicTypeKind(reflect.Int)
+ Int8Kind = BasicTypeKind(reflect.Int8)
+ Int16Kind = BasicTypeKind(reflect.Int16)
+ Int32Kind = BasicTypeKind(reflect.Int32)
+ Int64Kind = BasicTypeKind(reflect.Int64)
+ UintKind = BasicTypeKind(reflect.Uint)
+ Uint8Kind = BasicTypeKind(reflect.Uint8)
+ Uint16Kind = BasicTypeKind(reflect.Uint16)
+ Uint32Kind = BasicTypeKind(reflect.Uint32)
+ Uint64Kind = BasicTypeKind(reflect.Uint64)
+ UintptrKind = BasicTypeKind(reflect.Uintptr)
+ Float32Kind = BasicTypeKind(reflect.Float32)
+ Float64Kind = BasicTypeKind(reflect.Float64)
+ Complex64Kind = BasicTypeKind(reflect.Complex64)
+ Complex128Kind = BasicTypeKind(reflect.Complex128)
+ StringKind = BasicTypeKind(reflect.String)
+ UnsafePointerKind = BasicTypeKind(reflect.UnsafePointer)
NilKind BasicTypeKind = StringKind + iota + 1
RuneKind
7 types/universe.go
View
@@ -134,16 +134,11 @@ func init() {
Unsafe = ast.NewObj(ast.Pkg, "unsafe")
Unsafe.Data = scope
- UnsafePointer = defType("Pointer", UintptrKind)
+ UnsafePointer = defType("Pointer", UnsafePointerKind)
defFun("Alignof")
- defFun("New")
- defFun("NewArray")
defFun("Offsetof")
- defFun("Reflect")
defFun("Sizeof")
- defFun("Typeof")
- defFun("Unreflect")
}
// vim: set ft=go :
4 value.go
View
@@ -446,7 +446,7 @@ func (v ConstValue) LLVMValue() llvm.Value {
fallthrough
case types.UntypedComplexKind:
panic("Attempting to take LLVM value of untyped constant")
- case types.Int32Kind, types.Uint32Kind, types.UintptrKind: // FIXME uintptr to become bitwidth dependent
+ case types.Int32Kind, types.Uint32Kind, types.UintptrKind, types.UnsafePointerKind: // FIXME uintptr to become bitwidth dependent
// XXX rune
return llvm.ConstInt(llvm.Int32Type(), uint64(v.Int64()), false)
case types.Int16Kind, types.Uint16Kind:
@@ -462,7 +462,7 @@ func (v ConstValue) LLVMValue() llvm.Value {
}
return llvm.ConstNull(llvm.Int1Type())
}
- panic("Unhandled type")
+ panic(fmt.Errorf("Unhandled type: %v", v.typ.Kind))
}
func (v ConstValue) Type() types.Type {
Please sign in to comment.
Something went wrong with that request. Please try again.