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 }