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 This file contains convenient functionality for reading/writing LLVM bitcode
7 and IR.
8 The functions prefer using D's stdlib for IO when possible.
9 */
10 module llvm_hiwrap.io;
11 
12 import std.stdio : File;
13 import llvm_hiwrap.module_ : Module;
14 import llvm_hiwrap.context : Context;
15 
16 /** Write a LLVM module to the file.
17  *
18  * The function seems silly. It is mostly for self documenting purpose.
19  *
20  * NOTE: The file extension should be ".bc".
21  * NOTE: I have confirmed that this function produces the same output as if
22  * LLVMWriteBitcodeToFile is called. See the unittests further down.
23  */
24 void writeModule(File f, ref Module m) {
25     auto buf = m.toBuffer;
26     f.rawWrite(buf.slice);
27 }
28 
29 @("shall be a dump of the module to a file")
30 unittest {
31     import std.file : remove;
32 
33     immutable p = "remove_me_module_dump_phobos.bc";
34     scope (exit)
35         remove(p);
36 
37     auto m = Module("as_file");
38     auto f = File(p, "w");
39     f.writeModule(m);
40 }
41 
42 @("shall be no difference between LLVM IO and when using phobos")
43 unittest {
44     import std.file;
45     import llvm_hiwrap.llvm_io;
46 
47     // arrange
48     immutable llvm_io_output = "remove_me_llmv_io_output.bc";
49     immutable hiwrap_io_output = "remove_me_hiwrap_io_output.bc";
50     auto test_module = Module("test");
51 
52     // act
53     test_module.dumpToFile(llvm_io_output);
54     scope (exit)
55         remove(llvm_io_output);
56 
57     File(hiwrap_io_output, "w").writeModule(test_module);
58     scope (exit)
59         remove(hiwrap_io_output);
60 
61     // assert
62     ubyte[] llvm_io_data = cast(ubyte[]) std.file.read(llvm_io_output);
63     ubyte[] hiwrap_io_data = cast(ubyte[]) std.file.read(hiwrap_io_output);
64 
65     assert(llvm_io_data == hiwrap_io_data);
66 }
67 
68 /** Read a LLVM module from the file.
69  *
70  */
71 auto readModule(File f, ref Context ctx, string module_id = null) {
72     import llvm_hiwrap.buffer;
73 
74     ubyte[1024] buf;
75     ubyte[] raw_data;
76     raw_data.reserve(1024);
77 
78     {
79         ubyte[] read_bytes;
80         do {
81             read_bytes = f.rawRead(buf);
82             raw_data ~= read_bytes;
83         }
84         while (read_bytes.length != 0);
85     }
86 
87     auto llvm_buf = MemoryBuffer.fromMemory(raw_data, module_id);
88 
89     return ctx.makeModule(llvm_buf);
90 }
91 
92 @("the written file shall be equivalent to the original when read back (serialize/deserialize)")
93 unittest {
94     import std.file;
95     import llvm_hiwrap.context;
96     import test_utils;
97 
98     // arrange
99     auto ctx = Context.make;
100     auto test_module = ctx.makeModule("foo");
101 
102     immutable test_module_output = "remove_me_test_module_file.bc";
103     File(test_module_output, "w").writeModule(test_module);
104     scope (exit)
105         remove(test_module_output);
106 
107     // act
108     auto read_module = File(test_module_output).readModule(ctx, "foo");
109 
110     // assert
111     assert(read_module.isValid);
112 
113     auto extracted_read_module = read_module.value;
114 
115     auto original_as_txt = test_module.toString;
116     auto read_as_txt = extracted_read_module.toString;
117 
118     assert(original_as_txt == read_as_txt);
119 }