package compiler

// This file defines alias functions for functions that are normally defined in
// Go assembly.
//
// The Go toolchain defines many performance critical functions in assembly
// instead of plain Go. This is a problem for TinyGo as it currently (as of
// august 2021) is not able to compile these assembly files and even if it
// could, it would not be able to make use of them for many targets that are
// supported by TinyGo (baremetal RISC-V, AVR, etc). Therefore, many of these
// functions are aliased to their generic Go implementation.
// This results in slower than possible implementations, but at least they are
// usable.

import "tinygo.org/x/go-llvm"

var stdlibAliases = map[string]string{
	// crypto packages
	"crypto/ed25519/internal/edwards25519/field.feMul": "crypto/ed25519/internal/edwards25519/field.feMulGeneric",
	"crypto/internal/edwards25519/field.feSquare":      "crypto/ed25519/internal/edwards25519/field.feSquareGeneric",
	"crypto/md5.block":         "crypto/md5.blockGeneric",
	"crypto/sha1.block":        "crypto/sha1.blockGeneric",
	"crypto/sha1.blockAMD64":   "crypto/sha1.blockGeneric",
	"crypto/sha256.block":      "crypto/sha256.blockGeneric",
	"crypto/sha512.blockAMD64": "crypto/sha512.blockGeneric",

	// AES
	"crypto/aes.decryptBlockAsm": "crypto/aes.decryptBlock",
	"crypto/aes.encryptBlockAsm": "crypto/aes.encryptBlock",

	// math package
	"math.archHypot": "math.hypot",
	"math.archMax":   "math.max",
	"math.archMin":   "math.min",
	"math.archModf":  "math.modf",
}

// createAlias implements the function (in the builder) as a call to the alias
// function.
func (b *builder) createAlias(alias llvm.Value) {
	b.llvmFn.SetVisibility(llvm.HiddenVisibility)
	b.llvmFn.SetUnnamedAddr(true)

	if b.Debug {
		if b.fn.Syntax() != nil {
			// Create debug info file if present.
			b.difunc = b.attachDebugInfo(b.fn)
		}
		pos := b.program.Fset.Position(b.fn.Pos())
		b.SetCurrentDebugLocation(uint(pos.Line), uint(pos.Column), b.difunc, llvm.Metadata{})
	}
	entryBlock := b.ctx.AddBasicBlock(b.llvmFn, "entry")
	b.SetInsertPointAtEnd(entryBlock)
	if b.llvmFn.Type() != alias.Type() {
		b.addError(b.fn.Pos(), "alias function should have the same type as aliasee "+alias.Name())
		b.CreateUnreachable()
		return
	}
	result := b.CreateCall(alias.GlobalValueType(), alias, b.llvmFn.Params(), "")
	if result.Type().TypeKind() == llvm.VoidTypeKind {
		b.CreateRetVoid()
	} else {
		b.CreateRet(result)
	}
}
