github.com/goplus/llgo@v0.8.3/ssa/stmt_builder.go (about)

     1  /*
     2   * Copyright (c) 2024 The GoPlus Authors (goplus.org). All rights reserved.
     3   *
     4   * Licensed under the Apache License, Version 2.0 (the "License");
     5   * you may not use this file except in compliance with the License.
     6   * You may obtain a copy of the License at
     7   *
     8   *     http://www.apache.org/licenses/LICENSE-2.0
     9   *
    10   * Unless required by applicable law or agreed to in writing, software
    11   * distributed under the License is distributed on an "AS IS" BASIS,
    12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13   * See the License for the specific language governing permissions and
    14   * limitations under the License.
    15   */
    16  
    17  package ssa
    18  
    19  import (
    20  	"bytes"
    21  	"fmt"
    22  	"go/types"
    23  	"log"
    24  	"strings"
    25  
    26  	"github.com/goplus/llvm"
    27  )
    28  
    29  // -----------------------------------------------------------------------------
    30  
    31  type aBasicBlock struct {
    32  	impl llvm.BasicBlock
    33  	fn   Function
    34  	idx  int
    35  }
    36  
    37  // BasicBlock represents a basic block in a function.
    38  type BasicBlock = *aBasicBlock
    39  
    40  // Parent returns the function to which the basic block belongs.
    41  func (p BasicBlock) Parent() Function {
    42  	return p.fn
    43  }
    44  
    45  // Index returns the index of the basic block in the parent function.
    46  func (p BasicBlock) Index() int {
    47  	return p.idx
    48  }
    49  
    50  // -----------------------------------------------------------------------------
    51  
    52  type aBuilder struct {
    53  	impl llvm.Builder
    54  	Func Function
    55  	Prog Program
    56  }
    57  
    58  // Builder represents a builder for creating instructions in a function.
    59  type Builder = *aBuilder
    60  
    61  // Dispose disposes of the builder.
    62  func (b Builder) Dispose() {
    63  	b.impl.Dispose()
    64  }
    65  
    66  // SetBlock means SetBlockEx(blk, AtEnd).
    67  func (b Builder) SetBlock(blk BasicBlock) Builder {
    68  	if debugInstr {
    69  		log.Printf("Block _llgo_%v:\n", blk.idx)
    70  	}
    71  	b.SetBlockEx(blk, AtEnd)
    72  	return b
    73  }
    74  
    75  type InsertPoint int
    76  
    77  const (
    78  	AtEnd InsertPoint = iota
    79  	AtStart
    80  	AfterInit
    81  )
    82  
    83  // SetBlockEx sets blk as current basic block and pos as its insert point.
    84  func (b Builder) SetBlockEx(blk BasicBlock, pos InsertPoint) Builder {
    85  	if b.Func != blk.fn {
    86  		panic("mismatched function")
    87  	}
    88  	switch pos {
    89  	case AtEnd:
    90  		b.impl.SetInsertPointAtEnd(blk.impl)
    91  	case AtStart:
    92  		b.impl.SetInsertPointBefore(blk.impl.FirstInstruction())
    93  	case AfterInit:
    94  		b.impl.SetInsertPointBefore(instrAfterInit(blk.impl))
    95  	default:
    96  		panic("SetBlockEx: invalid pos")
    97  	}
    98  	return b
    99  }
   100  
   101  func instrAfterInit(blk llvm.BasicBlock) llvm.Value {
   102  	instr := blk.FirstInstruction()
   103  	for {
   104  		instr = llvm.NextInstruction(instr)
   105  		if notInit(instr) {
   106  			return instr
   107  		}
   108  	}
   109  }
   110  
   111  func notInit(instr llvm.Value) bool {
   112  	switch op := instr.InstructionOpcode(); op {
   113  	case llvm.Call:
   114  		if n := instr.OperandsCount(); n == 1 {
   115  			fn := instr.Operand(0)
   116  			return !strings.HasSuffix(fn.Name(), ".init")
   117  		}
   118  	}
   119  	return true
   120  }
   121  
   122  // Panic emits a panic instruction.
   123  func (b Builder) Panic(v Expr) {
   124  	if debugInstr {
   125  		log.Printf("Panic %v\n", v.impl)
   126  	}
   127  	pkg := b.Func.Pkg
   128  	b.Call(pkg.rtFunc("TracePanic"), v)
   129  	b.impl.CreateUnreachable()
   130  }
   131  
   132  // Unreachable emits an unreachable instruction.
   133  func (b Builder) Unreachable() {
   134  	b.impl.CreateUnreachable()
   135  }
   136  
   137  // Return emits a return instruction.
   138  func (b Builder) Return(results ...Expr) {
   139  	if debugInstr {
   140  		var b bytes.Buffer
   141  		fmt.Fprint(&b, "Return ")
   142  		for i, arg := range results {
   143  			if i > 0 {
   144  				fmt.Fprint(&b, ", ")
   145  			}
   146  			fmt.Fprint(&b, arg.impl)
   147  		}
   148  		log.Println(b.String())
   149  	}
   150  	switch n := len(results); n {
   151  	case 0:
   152  		b.impl.CreateRetVoid()
   153  	case 1:
   154  		raw := b.Func.raw.Type.(*types.Signature).Results().At(0).Type()
   155  		ret := checkExpr(results[0], raw, b)
   156  		b.impl.CreateRet(ret.impl)
   157  	default:
   158  		tret := b.Func.raw.Type.(*types.Signature).Results()
   159  		b.impl.CreateAggregateRet(llvmParams(0, results, tret, b))
   160  	}
   161  }
   162  
   163  // Jump emits a jump instruction.
   164  func (b Builder) Jump(jmpb BasicBlock) {
   165  	if b.Func != jmpb.fn {
   166  		panic("mismatched function")
   167  	}
   168  	if debugInstr {
   169  		log.Printf("Jump _llgo_%v\n", jmpb.idx)
   170  	}
   171  	b.impl.CreateBr(jmpb.impl)
   172  }
   173  
   174  // If emits an if instruction.
   175  func (b Builder) If(cond Expr, thenb, elseb BasicBlock) {
   176  	if b.Func != thenb.fn || b.Func != elseb.fn {
   177  		panic("mismatched function")
   178  	}
   179  	if debugInstr {
   180  		log.Printf("If %v, _llgo_%v, _llgo_%v\n", cond.impl, thenb.idx, elseb.idx)
   181  	}
   182  	b.impl.CreateCondBr(cond.impl, thenb.impl, elseb.impl)
   183  }
   184  
   185  // The MapUpdate instruction updates the association of Map[Key] to
   186  // Value.
   187  //
   188  // Pos() returns the ast.KeyValueExpr.Colon or ast.IndexExpr.Lbrack,
   189  // if explicit in the source.
   190  //
   191  // Example printed form:
   192  //
   193  //	t0[t1] = t2
   194  func (b Builder) MapUpdate(m, k, v Expr) {
   195  	if debugInstr {
   196  		log.Printf("MapUpdate %v[%v] = %v\n", m.impl, k.impl, v.impl)
   197  	}
   198  	// TODO(xsw)
   199  	// panic("todo")
   200  }
   201  
   202  // -----------------------------------------------------------------------------