Skip to content

Commit 1e4b859

Browse files
committed
Add wrappers for LLVM's cloning utilities.
1 parent 1b2da9d commit 1e4b859

File tree

11 files changed

+323
-86
lines changed

11 files changed

+323
-86
lines changed

deps/LLVMExtra/include/LLVMExtra.h

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,5 +115,21 @@ LLVMTypeRef LLVMGetTypeAttributeValue(LLVMAttributeRef A);
115115
LLVMBool LLVMIsTypeAttribute(LLVMAttributeRef A);
116116
#endif
117117

118+
typedef enum {
119+
LLVMCloneFunctionChangeTypeLocalChangesOnly = 0,
120+
LLVMCloneFunctionChangeTypeGlobalChanges = 1,
121+
LLVMCloneFunctionChangeTypeDifferentModule = 2,
122+
LLVMCloneFunctionChangeTypeClonedModule = 3
123+
} LLVMCloneFunctionChangeType;
124+
125+
void LLVMCloneFunctionInto(LLVMValueRef NewFunc, LLVMValueRef OldFunc,
126+
LLVMValueRef *ValueMap, unsigned ValueMapElements,
127+
LLVMCloneFunctionChangeType Changes,
128+
const char *NameSuffix,
129+
LLVMTypeRef (*TypeMapper)(LLVMTypeRef, void *),
130+
void *TypeMapperData,
131+
LLVMValueRef (*Materializer)(LLVMValueRef, void *),
132+
void *MaterializerData);
133+
118134
LLVM_C_EXTERN_C_END
119135
#endif

deps/LLVMExtra/lib/llvm-api.cpp

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
#include <llvm/IR/Function.h>
99
#include <llvm/IR/GlobalValue.h>
1010
#include <llvm/IR/Instruction.h>
11+
#include <llvm/IR/Instructions.h>
1112
#include <llvm/IR/LegacyPassManager.h>
1213
#include <llvm/IR/Module.h>
1314
#include <llvm/Support/TargetSelect.h>
@@ -17,6 +18,7 @@
1718
#if LLVM_VERSION_MAJOR < 12
1819
#include <llvm/Transforms/Scalar/InstSimplifyPass.h>
1920
#endif
21+
#include <llvm/Transforms/Utils/Cloning.h>
2022
#include <llvm/Transforms/Utils/ModuleUtils.h>
2123

2224
using namespace llvm;
@@ -363,3 +365,52 @@ LLVMBool LLVMIsTypeAttribute(LLVMAttributeRef A) {
363365
return unwrap(A).isTypeAttribute();
364366
}
365367
#endif
368+
369+
class ExternalTypeRemapper : public ValueMapTypeRemapper {
370+
public:
371+
ExternalTypeRemapper(LLVMTypeRef (*fptr)(LLVMTypeRef, void *), void *data)
372+
: fptr(fptr), data(data) {}
373+
374+
private:
375+
Type *remapType(Type *SrcTy) override {
376+
return unwrap(fptr(wrap(SrcTy), data));
377+
}
378+
379+
LLVMTypeRef (*fptr)(LLVMTypeRef, void *);
380+
void *data;
381+
};
382+
383+
class ExternalValueMaterializer : public ValueMaterializer {
384+
public:
385+
ExternalValueMaterializer(LLVMValueRef (*fptr)(LLVMValueRef, void *),
386+
void *data)
387+
: fptr(fptr), data(data) {}
388+
389+
private:
390+
Value *materialize(Value *V) override { return unwrap(fptr(wrap(V), data)); }
391+
392+
LLVMValueRef (*fptr)(LLVMValueRef, void *);
393+
void *data;
394+
};
395+
396+
void LLVMCloneFunctionInto(LLVMValueRef NewFunc, LLVMValueRef OldFunc,
397+
LLVMValueRef *ValueMap, unsigned ValueMapElements,
398+
LLVMCloneFunctionChangeType Changes,
399+
const char *NameSuffix,
400+
LLVMTypeRef (*TypeMapper)(LLVMTypeRef, void *),
401+
void *TypeMapperData,
402+
LLVMValueRef (*Materializer)(LLVMValueRef, void *),
403+
void *MaterializerData) {
404+
// NOTE: we ignore returns cloned, and don't return the code info
405+
SmallVector<ReturnInst *, 8> Returns;
406+
407+
ValueToValueMapTy VMap;
408+
for (unsigned i = 0; i < ValueMapElements; ++i)
409+
VMap[unwrap(ValueMap[2 * i])] = unwrap(ValueMap[2 * i + 1]);
410+
ExternalTypeRemapper TheTypeRemapper(TypeMapper, TypeMapperData);
411+
ExternalValueMaterializer TheMaterializer(Materializer, MaterializerData);
412+
CloneFunctionInto(unwrap<Function>(NewFunc), unwrap<Function>(OldFunc), VMap,
413+
Changes, Returns, NameSuffix, nullptr,
414+
TypeMapper ? &TheTypeRemapper : nullptr,
415+
Materializer ? &TheMaterializer : nullptr);
416+
}

lib/libLLVM_extra.jl

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -323,3 +323,14 @@ function LLVMOrcLLJITApplyDataLayout(J, Mod)
323323
end
324324

325325
end # version
326+
327+
@cenum LLVMCloneFunctionChangeType::UInt32 begin
328+
LLVMCloneFunctionChangeTypeLocalChangesOnly = 0
329+
LLVMCloneFunctionChangeTypeGlobalChanges = 1
330+
LLVMCloneFunctionChangeTypeDifferentModule = 2
331+
LLVMCloneFunctionChangeTypeClonedModule = 3
332+
end
333+
334+
function LLVMCloneFunctionInto(NewFunc, OldFunc, ValueMap, ValueMapElements, Changes, NameSuffix, TypeMapper, TypeMapperData, Materializer, MaterializerData)
335+
ccall((:LLVMCloneFunctionInto, libLLVMExtra), Cvoid, (LLVMValueRef, LLVMValueRef, Ptr{LLVMValueRef}, Cuint, LLVMCloneFunctionChangeType, Cstring, Ptr{Cvoid}, Ptr{Cvoid}, Ptr{Cvoid}, Ptr{Cvoid}), NewFunc, OldFunc, ValueMap, ValueMapElements, Changes, NameSuffix, TypeMapper, TypeMapperData, Materializer, MaterializerData)
336+
end

src/LLVM.jl

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@ using Libdl
77

88
## source code includes
99

10-
include("util.jl")
1110
include("base.jl")
11+
include("version.jl")
1212

1313
const libllvm = Ref{String}()
1414

@@ -60,6 +60,7 @@ include("transform.jl")
6060
include("debuginfo.jl")
6161
include("dibuilder.jl")
6262
include("jitevents.jl")
63+
include("utils.jl")
6364

6465
has_orc_v1() = v"8" <= LLVM.version() < v"12"
6566
if has_orc_v1()

src/base.jl

Lines changed: 64 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,67 @@
1-
# Basic library functionality
2-
3-
export version
4-
5-
version() = Base.libllvm_version
6-
7-
function runtime_version()
8-
# FIXME: add a proper C API to LLVM
9-
version_print = unsafe_string(
10-
ccall((:_ZN4llvm16LTOCodeGenerator16getVersionStringEv, libllvm[]), Cstring, ()))
11-
m = match(r"LLVM version (?<version>.+)", version_print)
12-
m === nothing && error("Unrecognized version string: '$version_print'")
13-
if endswith(m[:version], "jl")
14-
# strip the "jl" SONAME suffix (JuliaLang/julia#33058)
15-
# (LLVM does never report a prerelease version anyway)
16-
VersionNumber(m[:version][1:end-2])
1+
# helpers for wrapping the library
2+
3+
function unsafe_message(ptr, args...)
4+
str = unsafe_string(ptr, args...)
5+
API.LLVMDisposeMessage(ptr)
6+
str
7+
end
8+
9+
10+
## defining types in the LLVM type hierarchy
11+
12+
# llvm.org/docs/doxygen/html/group__LLVMCSupportTypes.html
13+
14+
# macro that adds an inner constructor to a type definition,
15+
# calling `refcheck` on the ref field argument
16+
macro checked(typedef)
17+
# decode structure definition
18+
if Meta.isexpr(typedef, :macrocall)
19+
# handle `@compat` prefixing 0.6-style type declarations
20+
typedef = macroexpand(typedef)
21+
end
22+
if Meta.isexpr(typedef, :struct)
23+
structure = typedef.args[2]
24+
body = typedef.args[3]
1725
else
18-
VersionNumber(m[:version])
26+
error("argument is not a structure definition")
1927
end
28+
if isa(structure, Symbol)
29+
# basic type definition
30+
typename = structure
31+
elseif Meta.isexpr(structure, :<:)
32+
# typename <: parentname
33+
all(e->isa(e,Symbol), structure.args) ||
34+
error("typedef should consist of plain types, ie. not parametric ones")
35+
typename = structure.args[1]
36+
else
37+
error("malformed type definition: cannot decode type name")
38+
end
39+
40+
# decode fields
41+
field_names = Symbol[]
42+
field_defs = Union{Symbol,Expr}[]
43+
for arg in body.args
44+
if isa(arg, LineNumberNode)
45+
continue
46+
elseif isa(arg, Symbol)
47+
push!(field_names, arg)
48+
push!(field_defs, arg)
49+
elseif Meta.isexpr(arg, :(::))
50+
push!(field_names, arg.args[1])
51+
push!(field_defs, arg)
52+
end
53+
end
54+
:ref in field_names || error("structure definition should contain 'ref' field")
55+
56+
# insert checked constructor
57+
push!(body.args, :(
58+
$typename($(field_defs...)) = (refcheck($typename, ref); new($(field_names...)))
59+
))
60+
61+
return esc(typedef)
62+
end
63+
64+
# the most basic check is asserting that we don't use a null pointer
65+
@inline function refcheck(::Type, ref::Ptr)
66+
ref==C_NULL && throw(UndefRefError())
2067
end

src/util.jl

Lines changed: 0 additions & 67 deletions
This file was deleted.

src/utils.jl

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
# Utilities for working with LLVM source code
2+
3+
## cloning
4+
5+
export clone_into!, clone
6+
7+
type_mapper_callback(typ, type_mapper) =
8+
Base.unsafe_convert(API.LLVMTypeRef, type_mapper[](LLVMType(typ)))
9+
materializer_callback(val, materializer) =
10+
Base.unsafe_convert(API.LLVMValueRef, materializer[](Value(val)))
11+
12+
function clone_into!(new::Function, old::Function;
13+
value_map::Dict{Value,Value}=Dict{Value,Value}(),
14+
changes=API.LLVMCloneFunctionChangeTypeLocalChangesOnly,
15+
suffix::String="", type_mapper=nothing, materializer=nothing)
16+
value_map_array = Value[]
17+
for (src, dest) in value_map
18+
push!(value_map_array, src)
19+
push!(value_map_array, dest)
20+
end
21+
if type_mapper === nothing
22+
type_mapper_ptr = C_NULL
23+
type_mapper_data = C_NULL
24+
else
25+
type_mapper_ptr = @cfunction(type_mapper_callback, API.LLVMTypeRef, (API.LLVMTypeRef,Any))
26+
type_mapper_data = Ref(type_mapper)
27+
end
28+
if materializer === nothing
29+
materializer_ptr = C_NULL
30+
materializer_data = C_NULL
31+
else
32+
materializer_ptr = @cfunction(materializer_callback, API.LLVMValueRef, (API.LLVMValueRef,Any))
33+
materializer_data = Ref(materializer)
34+
end
35+
API.LLVMCloneFunctionInto(new, old, value_map_array, length(value_map), changes, suffix,
36+
type_mapper_ptr, type_mapper_data,
37+
materializer_ptr, materializer_data)
38+
end
39+
40+
function clone(f::Function; value_map::Dict{Value,Value}=Dict{Value,Value}())
41+
argtypes = LLVMType[]
42+
43+
# The user might be deleting arguments to the function by specifying them in
44+
# the VMap. If so, we need to not add the arguments to the arg ty vector
45+
for arg in parameters(f)
46+
if !in(arg, keys(value_map)) # Haven't mapped the argument to anything yet?
47+
push!(argtypes, llvmtype(arg))
48+
end
49+
end
50+
51+
# Create a new function type...
52+
fty = FunctionType(return_type(eltype(llvmtype(f))), argtypes) # TODO: isVarArg
53+
54+
# Create the new function...
55+
new_f = Function(parent(f), name(f), fty)
56+
# TODO: address space, linkage
57+
58+
# Loop over the arguments, copying the names of the mapped arguments over...
59+
for (arg, new_arg) in zip(parameters(f), parameters(new_f))
60+
if !in(arg, keys(value_map)) # Is this argument preserved?
61+
name!(new_arg, name(arg)) # Copy the name over...
62+
value_map[arg] = new_arg # Add mapping to VMap
63+
end
64+
end
65+
66+
clone_into!(new_f, f;
67+
value_map, changes=API.LLVMCloneFunctionChangeTypeLocalChangesOnly)
68+
69+
return new_f
70+
end

src/version.jl

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
# Version management
2+
3+
export version
4+
5+
version() = Base.libllvm_version
6+
7+
function runtime_version()
8+
# FIXME: add a proper C API to LLVM
9+
version_print = unsafe_string(
10+
ccall((:_ZN4llvm16LTOCodeGenerator16getVersionStringEv, libllvm[]), Cstring, ()))
11+
m = match(r"LLVM version (?<version>.+)", version_print)
12+
m === nothing && error("Unrecognized version string: '$version_print'")
13+
if endswith(m[:version], "jl")
14+
# strip the "jl" SONAME suffix (JuliaLang/julia#33058)
15+
# (LLVM does never report a prerelease version anyway)
16+
VersionNumber(m[:version][1:end-2])
17+
else
18+
VersionNumber(m[:version])
19+
end
20+
end
File renamed without changes.

test/runtests.jl

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ using Test
1818
# because the context has been disposed already. avoid that by disabling `dispose`
1919
LLVM.dispose(::Context) = return
2020

21-
include("util.jl")
21+
include("helpers.jl")
2222

2323
@testset "types" begin
2424
@test convert(Bool, LLVM.True) == true
@@ -71,6 +71,7 @@ include("target.jl")
7171
include("targetmachine.jl")
7272
include("datalayout.jl")
7373
include("debuginfo.jl")
74+
include("utils.jl")
7475
if LLVM.has_orc_v1()
7576
include("orc.jl")
7677
end

0 commit comments

Comments
 (0)