forked from OSchip/llvm-project
178 lines
4.8 KiB
Go
178 lines
4.8 KiB
Go
//===- attribute.go - attribute processor ---------------------------------===//
|
|
//
|
|
// The LLVM Compiler Infrastructure
|
|
//
|
|
// This file is distributed under the University of Illinois Open Source
|
|
// License. See LICENSE.TXT for details.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
//
|
|
// This file processes llgo and //extern attributes.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
package irgen
|
|
|
|
import (
|
|
"fmt"
|
|
"go/ast"
|
|
"llvm.org/llvm/bindings/go/llvm"
|
|
"strings"
|
|
)
|
|
|
|
const AttributeCommentPrefix = "#llgo "
|
|
|
|
// Attribute represents an attribute associated with a
|
|
// global variable or function.
|
|
type Attribute interface {
|
|
Apply(llvm.Value)
|
|
}
|
|
|
|
// parseAttribute parses zero or more #llgo comment attributes associated with
|
|
// a global variable or function. The comment group provided will be processed
|
|
// one line at a time using parseAttribute.
|
|
func parseAttributes(doc *ast.CommentGroup) []Attribute {
|
|
var attributes []Attribute
|
|
if doc == nil {
|
|
return attributes
|
|
}
|
|
for _, comment := range doc.List {
|
|
if strings.HasPrefix(comment.Text, "//extern ") {
|
|
nameattr := nameAttribute(strings.TrimSpace(comment.Text[9:]))
|
|
attributes = append(attributes, nameattr)
|
|
continue
|
|
}
|
|
text := comment.Text[2:]
|
|
if strings.HasPrefix(comment.Text, "/*") {
|
|
text = text[:len(text)-2]
|
|
}
|
|
attr := parseAttribute(strings.TrimSpace(text))
|
|
if attr != nil {
|
|
attributes = append(attributes, attr)
|
|
}
|
|
}
|
|
return attributes
|
|
}
|
|
|
|
// parseAttribute parses a single #llgo comment attribute associated with
|
|
// a global variable or function. The string provided will be parsed
|
|
// if it begins with AttributeCommentPrefix, otherwise nil is returned.
|
|
func parseAttribute(line string) Attribute {
|
|
if !strings.HasPrefix(line, AttributeCommentPrefix) {
|
|
return nil
|
|
}
|
|
line = strings.TrimSpace(line[len(AttributeCommentPrefix):])
|
|
colon := strings.IndexRune(line, ':')
|
|
var key, value string
|
|
if colon == -1 {
|
|
key = line
|
|
} else {
|
|
key, value = line[:colon], line[colon+1:]
|
|
}
|
|
switch key {
|
|
case "linkage":
|
|
return parseLinkageAttribute(value)
|
|
case "name":
|
|
return nameAttribute(strings.TrimSpace(value))
|
|
case "attr":
|
|
return parseLLVMAttribute(strings.TrimSpace(value))
|
|
case "thread_local":
|
|
return tlsAttribute{}
|
|
default:
|
|
// FIXME decide what to do here. return error? log warning?
|
|
panic("unknown attribute key: " + key)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
type linkageAttribute llvm.Linkage
|
|
|
|
func (a linkageAttribute) Apply(v llvm.Value) {
|
|
v.SetLinkage(llvm.Linkage(a))
|
|
}
|
|
|
|
func parseLinkageAttribute(value string) linkageAttribute {
|
|
var result linkageAttribute
|
|
value = strings.Replace(value, ",", " ", -1)
|
|
for _, field := range strings.Fields(value) {
|
|
switch strings.ToLower(field) {
|
|
case "private":
|
|
result |= linkageAttribute(llvm.PrivateLinkage)
|
|
case "internal":
|
|
result |= linkageAttribute(llvm.InternalLinkage)
|
|
case "available_externally":
|
|
result |= linkageAttribute(llvm.AvailableExternallyLinkage)
|
|
case "linkonce":
|
|
result |= linkageAttribute(llvm.LinkOnceAnyLinkage)
|
|
case "common":
|
|
result |= linkageAttribute(llvm.CommonLinkage)
|
|
case "weak":
|
|
result |= linkageAttribute(llvm.WeakAnyLinkage)
|
|
case "appending":
|
|
result |= linkageAttribute(llvm.AppendingLinkage)
|
|
case "extern_weak":
|
|
result |= linkageAttribute(llvm.ExternalWeakLinkage)
|
|
case "linkonce_odr":
|
|
result |= linkageAttribute(llvm.LinkOnceODRLinkage)
|
|
case "weak_odr":
|
|
result |= linkageAttribute(llvm.WeakODRLinkage)
|
|
case "external":
|
|
result |= linkageAttribute(llvm.ExternalLinkage)
|
|
}
|
|
}
|
|
return result
|
|
}
|
|
|
|
type nameAttribute string
|
|
|
|
func (a nameAttribute) Apply(v llvm.Value) {
|
|
if !v.IsAFunction().IsNil() {
|
|
name := string(a)
|
|
curr := v.GlobalParent().NamedFunction(name)
|
|
if !curr.IsNil() && curr != v {
|
|
if curr.BasicBlocksCount() != 0 {
|
|
panic(fmt.Errorf("Want to take the name %s from a function that has a body!", name))
|
|
}
|
|
curr.SetName(name + "_llgo_replaced")
|
|
curr.ReplaceAllUsesWith(llvm.ConstBitCast(v, curr.Type()))
|
|
}
|
|
v.SetName(name)
|
|
} else {
|
|
v.SetName(string(a))
|
|
}
|
|
}
|
|
|
|
func parseLLVMAttribute(value string) llvmAttribute {
|
|
var result llvmAttribute
|
|
value = strings.Replace(value, ",", " ", -1)
|
|
for _, field := range strings.Fields(value) {
|
|
switch strings.ToLower(field) {
|
|
case "noreturn":
|
|
result |= llvmAttribute(llvm.NoReturnAttribute)
|
|
case "nounwind":
|
|
result |= llvmAttribute(llvm.NoUnwindAttribute)
|
|
case "noinline":
|
|
result |= llvmAttribute(llvm.NoInlineAttribute)
|
|
case "alwaysinline":
|
|
result |= llvmAttribute(llvm.AlwaysInlineAttribute)
|
|
}
|
|
}
|
|
return result
|
|
}
|
|
|
|
type llvmAttribute llvm.Attribute
|
|
|
|
func (a llvmAttribute) Apply(v llvm.Value) {
|
|
if !v.IsAFunction().IsNil() {
|
|
v.AddFunctionAttr(llvm.Attribute(a))
|
|
} else {
|
|
v.AddAttribute(llvm.Attribute(a))
|
|
}
|
|
}
|
|
|
|
type tlsAttribute struct{}
|
|
|
|
func (tlsAttribute) Apply(v llvm.Value) {
|
|
v.SetThreadLocal(true)
|
|
}
|