Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions PtrToAddrBindings.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
#include "PtrToAddrBindings.h"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P2] Missing LLVM license header

Same as the header file: this .cpp is missing the standard //===- ... ===// Apache-2.0-WITH-LLVM-exception block that every other binding source in the repo carries.

#include "llvm/Config/llvm-config.h"

#if LLVM_VERSION_MAJOR >= 22
#include "llvm/IR/Constants.h"
#include "llvm/IR/IRBuilder.h"

using namespace llvm;

LLVMValueRef LLVMGoBuildPtrToAddr(LLVMBuilderRef B, LLVMValueRef V,
const char *Name) {
return wrap(unwrap(B)->CreatePtrToAddr(unwrap(V), Name));
}

LLVMValueRef LLVMGoConstPtrToAddr(LLVMValueRef V, LLVMTypeRef T) {
return wrap(ConstantExpr::getPtrToAddr(unwrap<Constant>(V), unwrap(T)));
}
#endif
18 changes: 18 additions & 0 deletions PtrToAddrBindings.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
#ifndef LLVM_BINDINGS_GO_PTRTOADDR_H

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P2] Missing LLVM license header in new C++ files

Every existing binding file (IRBindings.h, SupportBindings.h, TargetBindings.h, etc.) opens with the standard //===- ... ===// Apache-2.0-WITH-LLVM-exception header block. Both PtrToAddrBindings.h and PtrToAddrBindings.cpp omit it. For an LLVM sub-project this header is effectively mandatory — please add the same block to both new files.

#define LLVM_BINDINGS_GO_PTRTOADDR_H

#include "llvm-c/Core.h"

#ifdef __cplusplus
extern "C" {
#endif

LLVMValueRef LLVMGoBuildPtrToAddr(LLVMBuilderRef B, LLVMValueRef V,
const char *Name);
LLVMValueRef LLVMGoConstPtrToAddr(LLVMValueRef V, LLVMTypeRef T);

#ifdef __cplusplus
}
#endif

#endif
32 changes: 32 additions & 0 deletions ptrtoaddr_llvm22.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
//go:build llvm22 || (!llvm14 && !llvm15 && !llvm16 && !llvm17 && !llvm18 && !llvm19 && !llvm20 && !llvm21)

package llvm

/*
#include "PtrToAddrBindings.h"
#include <stdlib.h>
*/
import "C"
import "unsafe"

const PtrToAddr Opcode = C.LLVMPtrToAddr

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P3] PtrToAddr Opcode const lives apart from the others in ir.go

All other Opcode constants — including the closely related PtrToInt Opcode = C.LLVMPtrToInt — live in the single const block in ir.go, so placing PtrToAddr here breaks discoverability. Keeping it separate is a genuine necessity (the enum only exists in the LLVM 22 C API, and ir.go is compiled unconditionally). Consider a one-line comment noting it is kept here rather than in ir.go because C.LLVMPtrToAddr only exists in LLVM 22+, to save the next maintainer the investigation.


// CreatePtrToAddr extracts the address of val without capturing its provenance.
// The result uses the pointer address space's index width from the containing
// module's DataLayout (and preserves the shape of pointer vectors). The builder
// must have an insertion point in a module with the intended DataLayout.
// Available with LLVM 22 and later.
func (b Builder) CreatePtrToAddr(val Value, name string) (v Value) {
cname := C.CString(name)
defer C.free(unsafe.Pointer(cname))
v.C = C.LLVMGoBuildPtrToAddr(b.C, val.C, cname)
return
}

// ConstPtrToAddr extracts a constant pointer's address without capturing its
// provenance. t must have the pointer address space's DataLayout index width
// and the same scalar or vector shape as val. Available with LLVM 22 and later.
func ConstPtrToAddr(val Value, t Type) (v Value) {
v.C = C.LLVMGoConstPtrToAddr(val.C, t.C)
return
}
108 changes: 108 additions & 0 deletions ptrtoaddr_llvm22_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
//go:build llvm22 || (!llvm14 && !llvm15 && !llvm16 && !llvm17 && !llvm18 && !llvm19 && !llvm20 && !llvm21)

package llvm

import (
"os"
"path/filepath"
"strings"
"testing"
)

func TestPtrToAddrDataLayoutAndRoundTrip(t *testing.T) {
ctx := NewContext()
defer ctx.Dispose()
mod := ctx.NewModule("ptrtoaddr")
defer mod.Dispose()
mod.SetDataLayout("e-p:64:64-p1:64:64:64:32")
builder := ctx.NewBuilder()
defer builder.Dispose()

for _, tc := range []struct {
name string
ptr Type
addr Type
}{
{"scalar", PointerType(ctx.Int8Type(), 0), ctx.Int64Type()},
{"narrow_index", PointerType(ctx.Int8Type(), 1), ctx.Int32Type()},
{"vector", VectorType(PointerType(ctx.Int8Type(), 1), 2), VectorType(ctx.Int32Type(), 2)},
} {
fn := AddFunction(mod, tc.name, FunctionType(tc.addr, []Type{tc.ptr}, false))
builder.SetInsertPointAtEnd(ctx.AddBasicBlock(fn, "entry"))
addr := builder.CreatePtrToAddr(fn.Param(0), "addr")
if addr.Type() != tc.addr || addr.InstructionOpcode() != PtrToAddr {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P3] Combined assertion shares one error message

This check tests two distinct properties (result type and opcode) but reports a single "unexpected ptrtoaddr" message. On failure the developer can't immediately tell whether the type or the opcode was wrong. Splitting into two checks, or including the expected-vs-actual type/opcode in the message, would improve diagnosability. Minor — the printed addr.String() already gives a strong hint.

t.Fatalf("%s: unexpected ptrtoaddr: %s", tc.name, addr.String())
}
builder.CreateRet(addr)
}

global := AddGlobal(mod, ctx.Int8Type(), "data")
addr := ConstPtrToAddr(global, ctx.Int64Type())
if addr.Opcode() != PtrToAddr {
t.Fatalf("unexpected constant opcode: %s", addr.String())
}
alias := AddGlobal(mod, ctx.Int64Type(), "address")
alias.SetInitializer(addr)
if err := VerifyModule(mod, ReturnStatusAction); err != nil {
t.Fatal(err)
}
path := filepath.Join(t.TempDir(), "ptrtoaddr.ll")
if err := os.WriteFile(path, []byte(mod.String()), 0600); err != nil {
t.Fatal(err)
}
buf, err := NewMemoryBufferFromFile(path)
if err != nil {
t.Fatal(err)
}
roundTrip, err := ctx.ParseIR(buf) // ParseIR consumes buf.
if err != nil {
t.Fatal(err)
}
defer roundTrip.Dispose()
if err := VerifyModule(roundTrip, ReturnStatusAction); err != nil {
t.Fatal(err)
}
if !strings.Contains(roundTrip.String(), "ptrtoaddr (ptr @data to i64)") {
t.Fatalf("constant expression lost during round trip:\n%s", roundTrip.String())
}
}

func TestPtrToAddrDoesNotExposeProvenance(t *testing.T) {
ctx := NewContext()
defer ctx.Dispose()
mod := ctx.NewModule("provenance")
defer mod.Dispose()
mod.SetDataLayout("e-p:64:64")
b := ctx.NewBuilder()
defer b.Dispose()
exposeTy := FunctionType(ctx.VoidType(), []Type{ctx.Int64Type()}, false)
expose := AddFunction(mod, "observe_address", exposeTy)
for _, name := range []string{"address", "pointer"} {
fn := AddFunction(mod, name, FunctionType(ctx.Int32Type(), nil, false))
b.SetInsertPointAtEnd(ctx.AddBasicBlock(fn, "entry"))
ptr := b.CreateAlloca(ctx.Int32Type(), "p")
b.CreateStore(ConstInt(ctx.Int32Type(), 7, false), ptr)
var addr Value
if name == "address" {
addr = b.CreatePtrToAddr(ptr, "addr")
} else {
addr = b.CreatePtrToInt(ptr, ctx.Int64Type(), "addr")
}
b.CreateCall(exposeTy, expose, []Value{addr}, "")
b.CreateRet(b.CreateLoad(ctx.Int32Type(), ptr, "value"))
}
options := NewPassBuilderOptions()
defer options.Dispose()
if err := mod.RunPasses("default<O2>", TargetMachine{}, options); err != nil {
t.Fatal(err)
}
if err := VerifyModule(mod, ReturnStatusAction); err != nil {
t.Fatal(err)
}
if ir := mod.NamedFunction("address").String(); !strings.Contains(ir, "ret i32 7") || strings.Contains(ir, "load i32") {
t.Fatalf("ptrtoaddr unexpectedly exposed provenance:\n%s", ir)
}
if ir := mod.NamedFunction("pointer").String(); !strings.Contains(ir, "load i32") {
t.Fatalf("ptrtoint control did not expose provenance:\n%s", ir)
}
}
Loading