blob: 1181ec0d0ebb87cd2632ae0fa69203bb9b4a6125 [file] [log] [blame]
// Copyright 2017 The Wuffs Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package cgen
import (
"errors"
"fmt"
a "github.com/google/wuffs/lang/ast"
t "github.com/google/wuffs/lang/token"
)
var errNeedDerivedVar = errors.New("cgen: internal error: need derived var")
// argsContainsArgsDotFoo returns whether the argNodes, a slice of *ast.Node
// where each ast.Node is an ast.Arg, contains a "name: value" element whose
// value is "args.foo".
func argsContainsArgsDotFoo(argNodes []*a.Node, foo t.ID) bool {
for _, o := range argNodes {
if foo == o.AsArg().Value().IsArgsDotFoo() {
return true
}
}
return false
}
func (g *gen) needDerivedVar(name t.ID) bool {
if name == 0 {
return false
}
for _, o := range g.currFunk.astFunc.Body() {
err := o.Walk(func(p *a.Node) error {
switch p.Kind() {
case a.KExpr:
// Look for p matching "args.name.etc(etc)".
recv, meth, args, ok := p.AsExpr().IsMethodCall()
if !ok {
return nil
}
if recv.IsArgsDotFoo() == name {
return errNeedDerivedVar
}
// Some built-in methods will also need a derived var for their
// arguments.
//
// TODO: use a comprehensive list of such methods.
switch meth {
case t.IDLimitedSwizzleU32InterleavedFromReader,
t.IDSwizzleInterleavedFromReader:
if recv.MType().Eq(typeExprPixelSwizzler) && argsContainsArgsDotFoo(args, name) {
return errNeedDerivedVar
}
}
case a.KIOManip:
if p.AsIOManip().IO().IsArgsDotFoo() == name {
return errNeedDerivedVar
}
}
return nil
})
if err == errNeedDerivedVar {
return true
}
}
return false
}
func (g *gen) findDerivedVars() {
for _, o := range g.currFunk.astFunc.In().Fields() {
o := o.AsField()
if !o.XType().IsIOTokenType() || !g.needDerivedVar(o.Name()) {
continue
}
if g.currFunk.derivedVars == nil {
g.currFunk.derivedVars = map[t.ID]struct{}{}
}
g.currFunk.derivedVars[o.Name()] = struct{}{}
}
}
func (g *gen) derivedVarCNames(typ *a.TypeExpr) (elem string, i1 string, i2 string, isWriter bool, retErr error) {
if typ.Decorator() == 0 {
if qid := typ.QID(); qid[0] == t.IDBase {
switch qid[1] {
case t.IDIOReader:
return "uint8_t", "meta.ri", "meta.wi", false, nil
case t.IDIOWriter:
return "uint8_t", "meta.wi", "data.len", true, nil
case t.IDTokenReader:
return "wuffs_base__token", "meta.ri", "meta.wi", false, nil
case t.IDTokenWriter:
return "wuffs_base__token", "meta.wi", "data.len", true, nil
}
}
}
return "", "", "", false, fmt.Errorf("unsupported derivedVarCNames type %q", typ.Str(g.tm))
}
func (g *gen) writeInitialLoadDerivedVar(b *buffer, n *a.Field) error {
elem, i1, i2, isWriter, err := g.derivedVarCNames(n.XType())
if err != nil {
return err
}
preName := aPrefix + n.Name().Str(g.tm)
c := ""
if !isWriter {
c = "const "
}
b.printf("%s%s* %s%s = NULL;\n", c, elem, iopPrefix, preName)
b.printf("%s%s* %s%s WUFFS_BASE__POTENTIALLY_UNUSED = NULL;\n", c, elem, io0Prefix, preName)
b.printf("%s%s* %s%s WUFFS_BASE__POTENTIALLY_UNUSED = NULL;\n", c, elem, io1Prefix, preName)
b.printf("%s%s* %s%s WUFFS_BASE__POTENTIALLY_UNUSED = NULL;\n", c, elem, io2Prefix, preName)
b.printf("if (%s) {\n", preName)
b.printf("%s%s = %s->data.ptr;\n", io0Prefix, preName, preName)
b.printf("%s%s = %s%s + %s->%s;\n", io1Prefix, preName, io0Prefix, preName, preName, i1)
b.printf("%s%s = %s%s;\n", iopPrefix, preName, io1Prefix, preName)
b.printf("%s%s = %s%s + %s->%s;\n", io2Prefix, preName, io0Prefix, preName, preName, i2)
if isWriter {
b.printf("if (%s->meta.closed) {\n", preName)
b.printf("%s%s = %s%s;\n", io2Prefix, preName, iopPrefix, preName)
b.printf("}\n")
}
b.printf("}\n")
return nil
}
func (g *gen) writeFinalSaveDerivedVar(b *buffer, n *a.Field) error {
_, i1, _, _, err := g.derivedVarCNames(n.XType())
if err != nil {
return err
}
preName := aPrefix + n.Name().Str(g.tm)
b.printf("if (%s) {\n%s->%s = ((size_t)(%s%s - %s->data.ptr));\n}\n",
preName, preName, i1, iopPrefix, preName, preName)
return nil
}
func (g *gen) writeLoadDerivedVar(b *buffer, n *a.Expr) error {
_, i1, _, _, err := g.derivedVarCNames(n.MType())
if err != nil {
return err
}
switch n.Operator() {
case 0:
name := n.Ident().Str(g.tm)
b.printf("%s%s%s = %s%s.data.ptr + %s%s.%s;\n",
iopPrefix, vPrefix, name, uPrefix, name, uPrefix, name, i1)
return nil
case t.IDDot:
if lhs := n.LHS().AsExpr(); (lhs.Operator() == 0) && (lhs.Ident() == t.IDArgs) {
name := n.Ident().Str(g.tm)
b.printf("if (%s%s) {\n", aPrefix, name)
b.printf("%s%s%s = %s%s->data.ptr + %s%s->%s;\n",
iopPrefix, aPrefix, name, aPrefix, name, aPrefix, name, i1)
b.printf("}\n")
return nil
}
}
return fmt.Errorf("could not determine derived variables for %q", n.Str(g.tm))
}
func (g *gen) writeSaveDerivedVar(b *buffer, n *a.Expr) error {
_, i1, _, _, err := g.derivedVarCNames(n.MType())
if err != nil {
return err
}
switch n.Operator() {
case 0:
name := n.Ident().Str(g.tm)
b.printf("%s%s.%s = ((size_t)(%s%s%s - %s%s.data.ptr));\n",
uPrefix, name, i1, iopPrefix, vPrefix, name, uPrefix, name)
return nil
case t.IDDot:
if lhs := n.LHS().AsExpr(); (lhs.Operator() == 0) && (lhs.Ident() == t.IDArgs) {
name := n.Ident().Str(g.tm)
b.printf("if (%s%s) {\n", aPrefix, name)
b.printf("%s%s->%s = ((size_t)(%s%s%s - %s%s->data.ptr));\n",
aPrefix, name, i1, iopPrefix, aPrefix, name, aPrefix, name)
b.printf("}\n")
return nil
}
}
return fmt.Errorf("could not determine derived variables for %q", n.Str(g.tm))
}
func (g *gen) couldHaveDerivedVar(n *a.Expr) bool {
typ := n.MType()
if typ.Decorator() != 0 {
return false
}
qid := typ.QID()
if qid[0] != t.IDBase {
return false
}
switch qid[1] {
case t.IDIOReader, t.IDIOWriter, t.IDTokenReader, t.IDTokenWriter:
// No-op.
default:
return false
}
switch n.Operator() {
case t.IDDot:
if lhs := n.LHS().AsExpr(); (lhs.Operator() == 0) && (lhs.Ident() == t.IDArgs) {
if g.currFunk.derivedVars == nil {
return false
} else if _, ok := g.currFunk.derivedVars[n.Ident()]; !ok {
return false
}
}
case t.IDOpenParen:
switch n.LHS().AsExpr().Ident() {
case t.IDEmptyIOReader, t.IDEmptyIOWriter:
if n.LHS().AsExpr().LHS().AsExpr().MType().IsEtcUtilityType() {
return false
}
}
}
return true
}
func (g *gen) writeLoadExprDerivedVars(b *buffer, n *a.Expr) error {
if (g.currFunk.derivedVars != nil) && (n.Operator() == a.ExprOperatorCall) {
for _, o := range n.Args() {
if v := o.AsArg().Value(); g.couldHaveDerivedVar(v) {
if err := g.writeLoadDerivedVar(b, v); err != nil {
return err
}
}
}
}
return nil
}
func (g *gen) writeSaveExprDerivedVars(b *buffer, n *a.Expr) error {
if (g.currFunk.derivedVars != nil) && (n.Operator() == a.ExprOperatorCall) {
for _, o := range n.Args() {
if v := o.AsArg().Value(); g.couldHaveDerivedVar(v) {
if err := g.writeSaveDerivedVar(b, v); err != nil {
return err
}
}
}
}
return nil
}
func (g *gen) writeResumeSuspend1(b *buffer, f *funk, n *a.Var, suspend bool) error {
if typ := n.XType(); typ.HasPointers() {
return nil
} else if f.varResumables == nil || !f.varResumables[n.Name()] {
return nil
} else {
local := fmt.Sprintf("%s%s", vPrefix, n.Name().Str(g.tm))
lhs := local
// TODO: don't hard-code [0], and allow recursive coroutines.
rhs := fmt.Sprintf("self->private_data.%s%s[0].%s", sPrefix, g.currFunk.astFunc.FuncName().Str(g.tm), lhs)
if suspend {
lhs, rhs = rhs, lhs
}
switch typ.Decorator() {
case 0:
b.printf("%s = %s;\n", lhs, rhs)
return nil
case t.IDArray:
inner := typ.Inner()
if inner.Decorator() != 0 {
break
}
qid := inner.QID()
if qid[0] != t.IDBase {
break
}
switch qid[1] {
case t.IDU8, t.IDU16, t.IDU32, t.IDU64:
b.printf("memcpy(%s, %s, sizeof(%s));\n", lhs, rhs, local)
return nil
}
}
}
return fmt.Errorf("cannot resume or suspend a local variable %q of type %q",
n.Name().Str(g.tm), n.XType().Str(g.tm))
}
func (g *gen) writeResumeSuspend(b *buffer, f *funk, suspend bool) error {
for _, n := range f.varList {
if err := g.writeResumeSuspend1(b, f, n, suspend); err != nil {
return err
}
}
return nil
}
func (g *gen) writeVars(b *buffer, f *funk, inStructDecl bool) error {
for _, n := range f.varList {
typ := n.XType()
if typ.Innermost().IsEtcUtilityType() {
continue
} else if inStructDecl &&
(typ.HasPointers() || f.varResumables == nil || !f.varResumables[n.Name()]) {
continue
}
name := n.Name().Str(g.tm)
if typ.IsIOType() {
b.printf("wuffs_base__io_buffer %s%s = wuffs_base__empty_io_buffer();\n", uPrefix, name)
} else if typ.IsTokenType() {
return fmt.Errorf("TODO: support token_{reader,writer} typed variables")
}
if err := g.writeCTypeName(b, typ, vPrefix, name); err != nil {
return err
}
if inStructDecl {
b.writes(";\n")
} else if typ.IsNumType() {
b.writes(" = 0;\n")
} else if typ.IsBool() {
b.writes(" = false;\n")
} else if typ.IsStatus() {
b.writes(" = wuffs_base__make_status(NULL);\n")
} else if typ.IsIOType() {
b.printf(" = &%s%s;\n", uPrefix, name)
} else if typ.Eq(typeExprARMCRC32U32) {
b.writes(" = 0;\n")
} else {
b.writes(" = {0};\n")
}
if typ.IsIOType() {
qualifier := ""
if typ.QID()[1] == t.IDIOReader {
qualifier = "const "
}
b.printf("%suint8_t* %s%s%s WUFFS_BASE__POTENTIALLY_UNUSED = NULL;\n",
qualifier, iopPrefix, vPrefix, name)
b.printf("%suint8_t* %s%s%s WUFFS_BASE__POTENTIALLY_UNUSED = NULL;\n",
qualifier, io0Prefix, vPrefix, name)
b.printf("%suint8_t* %s%s%s WUFFS_BASE__POTENTIALLY_UNUSED = NULL;\n",
qualifier, io1Prefix, vPrefix, name)
b.printf("%suint8_t* %s%s%s WUFFS_BASE__POTENTIALLY_UNUSED = NULL;\n",
qualifier, io2Prefix, vPrefix, name)
}
}
return nil
}