1 /**
2 Copyright: Copyright (c) 2017, Joakim Brännström. All rights reserved.
3 License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0)
4 Author: Joakim Brännström (joakim.brannstrom@gmx.com)
5 
6 partof: #SPC-llvm_hiwrap_module_error_handling
7 Diagnostic messages are part of the created module result to make it easy for
8 the user to find those specific messages created during the module creation.
9 */
10 module llvm_hiwrap.context;
11 
12 import llvm;
13 
14 struct Context {
15     import llvm_hiwrap.buffer : MemoryBuffer;
16     import llvm_hiwrap.module_;
17     import llvm_hiwrap.value.metadata;
18 
19     LLVMContextRef lx;
20     alias lx this;
21 
22     /// Collected diagnostic messages during usage.
23     DiagnosticSet diagnostic;
24 
25     /// Make an empty LLVM context.
26     static auto make() {
27         return Context(LLVMContextCreate);
28     }
29 
30     @disable this();
31     @disable this(this);
32 
33     /** Wrap an existing context and thus providing deterministic resource
34      * handling.
35      */
36     this(LLVMContextRef lx) {
37         this.lx = lx;
38         LLVMContextSetDiagnosticHandler(lx,
39                 &DiagnosticSet.dextoolLLVM_HandleDiagnostic, cast(void*)&diagnostic);
40     }
41 
42     ~this() {
43         LLVMContextDispose(lx);
44     }
45 
46     /** Create a new, empty module in a specific context.
47      */
48     Module makeModule(string id) {
49         import std..string : toStringz;
50 
51         auto s = id.toStringz;
52         return Module(LLVMModuleCreateWithNameInContext(s, lx));
53     }
54 
55     /** Make a `Module` from the specified `MemoryBuffer`.
56      *
57      * Assumtion: the memory buffer is only needed to create the module. It do
58      * not have to be kept alive after the function has finished. This is in
59      * contrast with the lazy API which would need to keep both alive.
60      *
61      * The ModuleID is derived from `buffer`.
62      *
63      * Diagnostic messages that are created by this operation become part of
64      * the result.
65      */
66     ModuleFromMemoryBufferResult makeModule(ref MemoryBuffer buffer) {
67         LLVMModuleRef m;
68         LLVMBool success;
69 
70         size_t curr_dia_idx = diagnostic.length;
71         if (curr_dia_idx != 0)
72             curr_dia_idx -= 1;
73 
74         static if (LLVM_Version >= asVersion(3, 8, 0)) {
75             success = LLVMParseBitcodeInContext2(lx, buffer.lx, &m);
76         } else static if (LLVM_Version < asVersion(3, 9, 0)) {
77             // this is deprecated.
78             // prefering the new one because it provides a `LxDiagnosticSeverity`.
79             LxMessage msg;
80             success = LLVMParseBitcodeInContext(lx, buffer.lx, &m, &msg.rawPtr);
81             if (success != 0) {
82                 // the severity is unknown therefor be extra causes by always
83                 // assuming the worst.
84                 diagnostic ~= Diagnostic(LxDiagnosticSeverity.LLVMDSError, msg.toD);
85             }
86         } else {
87             static assert(0, "should never happen");
88         }
89 
90         Diagnostic[] msgs;
91         if (curr_dia_idx < diagnostic.length) {
92             msgs = diagnostic[curr_dia_idx .. $];
93             diagnostic = diagnostic[0 .. curr_dia_idx + 1];
94         }
95 
96         // Success is funky. _false_ mean that there are no errors.
97         // From the documentation llvm-c/BitReader.h:
98         // Returns 0 on success.
99         return ModuleFromMemoryBufferResult(Module(m), success == 0, msgs);
100     }
101 
102     /** Obtain a MDString value.
103      *
104      * The returned instance corresponds to the llvm::MDString class.
105      */
106     MetadataStringValue metadataString(string name) {
107         import llvm_hiwrap.types;
108 
109         return LLVMMDStringInContext(lx, cast(const(char)*) name.ptr, cast(uint) name.length)
110             .LxValue.LxMetadataStringValue.MetadataStringValue;
111     }
112 
113     /** Resolve the referenced nodes a NamedMetadata consist of.
114      *
115      * The returned value corresponds to the llvm::MDNode class which has
116      * operands.
117      */
118     ResolvedNamedMetadataValue resolveNamedMetadata(ref NamedMetadataValue nmd) {
119         import std.array : array;
120         import std.algorithm : map;
121         import llvm_hiwrap.types;
122 
123         //TODO refactor to remove this GC use.
124         LLVMValueRef[] ops = nmd.map!(a => a.rawPtr).array();
125         auto raw = LLVMMDNodeInContext(lx, ops.ptr, cast(uint) ops.length);
126         return raw.LxValue.LxMetadataNodeValue.LxResolvedNamedMetadataValue
127             .ResolvedNamedMetadataValue;
128     }
129 }
130 
131 struct Diagnostic {
132     import std.conv : to;
133     import llvm_hiwrap.types : LxMessage, LxDiagnosticSeverity;
134     import llvm_hiwrap.util : toD;
135 
136     this(LLVMDiagnosticInfoRef di) {
137         auto m = LxMessage(LLVMGetDiagInfoDescription(di));
138         msg = m.toD;
139         auto raw_severity = LLVMGetDiagInfoSeverity(di);
140         severity = raw_severity.to!LxDiagnosticSeverity;
141     }
142 
143     LxDiagnosticSeverity severity;
144     string msg;
145 }
146 
147 struct DiagnosticSet {
148     Diagnostic[] value;
149     alias value this;
150 
151     extern (C) static void dextoolLLVM_HandleDiagnostic(LLVMDiagnosticInfoRef info, void* ctx) {
152         auto set = cast(DiagnosticSet*) ctx;
153         set.value ~= Diagnostic(info);
154     }
155 }
156 
157 private:
158 
159 struct ModuleFromMemoryBufferResult {
160     import llvm_hiwrap.module_;
161 
162     private Module value_;
163 
164     /// When this is false there may exist diagnostic messages.
165     bool isValid;
166     Diagnostic[] diagnostic;
167 
168     @disable this(this);
169 
170     auto value() {
171         assert(isValid);
172         auto lx = value_.lx;
173         value_.lx = null;
174         return Module(lx);
175     }
176 }