| //===- attribute.go - attribute processor ---------------------------------===// |
| // |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| // See https://llvm.org/LICENSE.txt for license information. |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| // |
| //===----------------------------------------------------------------------===// |
| // |
| // 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 "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)) |
| } |
| } |
| |
| type tlsAttribute struct{} |
| |
| func (tlsAttribute) Apply(v llvm.Value) { |
| v.SetThreadLocal(true) |
| } |