Hi all,
Recently, I managed to get this to work:
x := []int{1, 2, 3}
x.reverse()
fmt.Println(x)
That is, when I run my modified compiler on this code, it prints [3, 2, 1].
Neat!
To be clear, I am *not* intending to get this merged upstream -- this is
just a fun exercise to learn my way around the compiler.
My approach was as follows. First, I added a reverse function to the
runtime package:
func reverse(et *_type, s slice) {
tmp := newobject(et) // swap scratch space
for i := 0; i < s.len/2; i++ {
j := s.len - i - 1
ei := add(s.array, uintptr(i)*et.size)
ej := add(s.array, uintptr(j)*et.size)
typedmemmove(et, tmp, ei)
typedmemmove(et, ei, ej)
typedmemmove(et, ej, tmp)
}
}
I added this function's name to gc/builtin/runtime.go and ran go generate to
update gc/builtin.go.
Next, I modified the lookdot function of typecheck.go, such that when a
method named "reverse" is looked for on a slice type, and no existing
method is found, a sentinel method with the appropriate type is returned:
if n.Left.Type == t || n.Left.Type.Sym == nil {
mt := methtype(t)
if mt != nil {
f2 = lookdot1(n, s, mt, mt.Methods(), dostrcmp)
}
// new code
if f2 == nil && t.IsSlice() {
if s.Name == "reverse" {
recv := types.NewField()
recv.Type = t
f2 = types.NewField()
f2.Sym = &types.Sym{
Name: "reverse",
Pkg: builtinpkg,
}
f2.Type = functypefield(recv, nil, nil)
}
}
}
Finally, I modified gc/walk.go to replace the method call with a call to
the runtime function:
case OCALLINTER, OCALLFUNC, OCALLMETH:
if n.Op == OCALLINTER {
usemethod(n)
markUsedIfaceMethod(n)
}
// new code
if n.Left != nil && n.Left.Sym.Def == nil {
methname := n.Left.Sym.Name
if strings.HasSuffix(methname, "reverse") {
s := n.Left.Left
fn := syslook("reverse")
fn = substArgTypes(fn, s.Type.Elem())
n = mkcall1(fn, nil, init, typename(s.Type.Elem()), s)
}
}
This approach works, but I feel like I'm bludgeoning a very sophisticated
system into doing something it was not designed to do. I'm hoping that a
more experienced compiler hacker can offer some guidance as to the "Right
Way" of doing this. If you were tasked with adding a builtin method to all
slices, how would you do it?
In particular, how do I handle inlining? Currently I have to disable all
inlining when compiling; otherwise, I get internal compiler error: no
function definition. I assume this is because the inlining phase runs
before the method call is replaced with a runtime call. Is it possible to
hook up the method call to the runtime function definition during the
typecheck phase?
Relatedly, I'm finding compiler development to be much more cumbersome than
the Go programming I'm accustomed to. Specifically, I'm currently
re-running make.bash after each change, which takes around 4 minutes on my
machine. Is it possible to test my changes without re-running make.bash,
ideally compiling in just a few seconds? Another issue is that gopls does
not seem to like operating on the compiler source, at least when used with
VS Code. What IDE do other compiler devs use?
Sorry for the barrage of questions -- I hope this is the right place to ask
them!
--
You received this message because you are subscribed to the Google Groups
"golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to [email protected].
To view this discussion on the web visit
https://groups.google.com/d/msgid/golang-nuts/ada79351-fffa-4de3-8b53-821ed0ba3ebcn%40googlegroups.com.