Skip to content

Commit c9492da

Browse files
authored
Merge pull request #257 from maleadt/tb/cloning
Add wrappers for LLVM's cloning utilities.
2 parents 1b2da9d + 836173b commit c9492da

File tree

13 files changed

+328
-89
lines changed

13 files changed

+328
-89
lines changed

Manifest.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,9 +34,9 @@ version = "1.3.0"
3434

3535
[[LLVMExtra_jll]]
3636
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
37-
git-tree-sha1 = "a9b1130c4728b0e462a1c28772954650039eb847"
37+
git-tree-sha1 = "2d5a0044d6505f4771b5c82de87393f0c9741154"
3838
uuid = "dad2f222-ce93-54a1-a47d-0025e8a3acab"
39-
version = "0.0.7+0"
39+
version = "0.0.8+0"
4040

4141
[[LibCURL]]
4242
deps = ["LibCURL_jll", "MozillaCACerts_jll"]

Project.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,5 +11,5 @@ Unicode = "4ec0a83e-493e-50e2-b9ac-8f72acf5a8f5"
1111

1212
[compat]
1313
CEnum = "0.2, 0.3, 0.4"
14-
LLVMExtra_jll = "~0.0.7"
14+
LLVMExtra_jll = "~0.0.8"
1515
julia = "1.6"

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: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
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+
vararg = isvararg(eltype(llvmtype(f)))
53+
fty = FunctionType(return_type(eltype(llvmtype(f))), argtypes; vararg)
54+
55+
# Create the new function...
56+
new_f = Function(parent(f), name(f), fty)
57+
linkage!(new_f, linkage(f))
58+
# TODO: address space
59+
60+
# Loop over the arguments, copying the names of the mapped arguments over...
61+
for (arg, new_arg) in zip(parameters(f), parameters(new_f))
62+
if !in(arg, keys(value_map)) # Is this argument preserved?
63+
name!(new_arg, name(arg)) # Copy the name over...
64+
value_map[arg] = new_arg # Add mapping to VMap
65+
end
66+
end
67+
68+
clone_into!(new_f, f;
69+
value_map, changes=API.LLVMCloneFunctionChangeTypeLocalChangesOnly)
70+
71+
return new_f
72+
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

0 commit comments

Comments
 (0)