1 //==-- loop_proto_to_llvm.cpp - Protobuf-C++ conversion
2 //---------------------==//
3 //
4 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
5 // See https://llvm.org/LICENSE.txt for license information.
6 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //
8 //===----------------------------------------------------------------------===//
9 //
10 // Implements functions for converting between protobufs and LLVM IR.
11 //
12 //
13 //===----------------------------------------------------------------------===//
14 
15 #include "loop_proto_to_llvm.h"
16 #include "cxx_loop_proto.pb.h"
17 #include "../handle-llvm/input_arrays.h"
18 
19 // The following is needed to convert protos in human-readable form
20 #include <google/protobuf/text_format.h>
21 
22 #include <ostream>
23 #include <sstream>
24 
25 namespace clang_fuzzer {
26 
27 // Forward decls
28 std::string BinopToString(std::ostream &os, const BinaryOp &x);
29 std::string StateSeqToString(std::ostream &os, const StatementSeq &x);
30 
31 // Counter variable to generate new LLVM IR variable names and wrapper function
get_var()32 static std::string get_var() {
33   static int ctr = 0;
34   return "%var" + std::to_string(ctr++);
35 }
36 
37 static bool inner_loop = false;
38 class InnerLoop {
39   public:
InnerLoop()40   InnerLoop() {
41     inner_loop = true;
42   }
~InnerLoop()43   ~InnerLoop() {
44     inner_loop = false;
45   }
46 };
47 
48 
49 // Proto to LLVM.
50 
ConstToString(const Const & x)51 std::string ConstToString(const Const &x) {
52   return std::to_string(x.val());
53 }
VarRefToString(std::ostream & os,const VarRef & x)54 std::string VarRefToString(std::ostream &os, const VarRef &x) {
55   std::string which_loop = inner_loop ? "inner" : "outer";
56   std::string arr;
57   switch(x.arr()) {
58   case VarRef::ARR_A:
59     arr = "%a";
60     break;
61   case VarRef::ARR_B:
62     arr = "%b";
63     break;
64   case VarRef::ARR_C:
65     arr = "%c";
66     break;
67   }
68   std::string ptr_var = get_var();
69   os << ptr_var << " = getelementptr inbounds i32, i32* " << arr
70      << ", i64 %" << which_loop << "_ct\n";
71   return ptr_var;
72 }
RvalueToString(std::ostream & os,const Rvalue & x)73 std::string RvalueToString(std::ostream &os, const Rvalue &x) {
74   if(x.has_cons())
75     return ConstToString(x.cons());
76   if(x.has_binop())
77     return BinopToString(os, x.binop());
78   if(x.has_varref()) {
79     std::string var_ref = VarRefToString(os, x.varref());
80     std::string val_var = get_var();
81     os << val_var << " = load i32, i32* " << var_ref << "\n";
82     return val_var;
83   }
84   return "1";
85 
86 }
BinopToString(std::ostream & os,const BinaryOp & x)87 std::string BinopToString(std::ostream &os, const BinaryOp &x) {
88   std::string left = RvalueToString(os, x.left());
89   std::string right = RvalueToString(os, x.right());
90   std::string op;
91   switch (x.op()) {
92   case BinaryOp::PLUS:
93     op = "add";
94     break;
95   case BinaryOp::MINUS:
96     op = "sub";
97     break;
98   case BinaryOp::MUL:
99     op = "mul";
100     break;
101   case BinaryOp::XOR:
102     op = "xor";
103     break;
104   case BinaryOp::AND:
105     op = "and";
106     break;
107   case BinaryOp::OR:
108     op = "or";
109     break;
110   // Support for Boolean operators will be added later
111   case BinaryOp::EQ:
112   case BinaryOp::NE:
113   case BinaryOp::LE:
114   case BinaryOp::GE:
115   case BinaryOp::LT:
116   case BinaryOp::GT:
117     op = "add";
118     break;
119   }
120   std::string val_var = get_var();
121   os << val_var << " = " << op << " i32 " << left << ", " << right << "\n";
122   return val_var;
123 }
operator <<(std::ostream & os,const AssignmentStatement & x)124 std::ostream &operator<<(std::ostream &os, const AssignmentStatement &x) {
125   std::string rvalue = RvalueToString(os, x.rvalue());
126   std::string var_ref = VarRefToString(os, x.varref());
127   return os << "store i32 " << rvalue << ", i32* " << var_ref << "\n";
128 }
operator <<(std::ostream & os,const Statement & x)129 std::ostream &operator<<(std::ostream &os, const Statement &x) {
130   return os << x.assignment();
131 }
operator <<(std::ostream & os,const StatementSeq & x)132 std::ostream &operator<<(std::ostream &os, const StatementSeq &x) {
133   for (auto &st : x.statements()) {
134     os << st;
135   }
136   return os;
137 }
NestedLoopToString(std::ostream & os,const LoopFunction & x)138 void NestedLoopToString(std::ostream &os, const LoopFunction &x) {
139   os << "target triple = \"x86_64-unknown-linux-gnu\"\n"
140      << "define void @foo(i32* %a, i32* %b, i32* noalias %c, i64 %s) {\n"
141      << "outer_loop_start:\n"
142      << "%cmp = icmp sgt i64 %s, 0\n"
143      << "br i1 %cmp, label %inner_loop_start, label %end\n"
144      << "outer_loop:\n"
145      << x.outer_statements()
146      << "%o_ct_new = add i64 %outer_ct, 1\n"
147      << "%jmp_outer = icmp eq i64 %o_ct_new, %s\n"
148      << "br i1 %jmp_outer, label %end, label %inner_loop_start\n"
149      << "inner_loop_start:\n"
150      << "%outer_ct = phi i64 [%o_ct_new, %outer_loop], [0, %outer_loop_start]\n"
151      << "br label %inner_loop\n"
152      << "inner_loop:\n"
153      << "%inner_ct = phi i64 [0, %inner_loop_start], [%i_ct_new, %inner_loop]\n";
154   {
155     InnerLoop IL;
156     os << x.inner_statements();
157   }
158   os << "%i_ct_new = add i64 %inner_ct, 1\n"
159      << "%jmp_inner = icmp eq i64 %i_ct_new, %s\n"
160      << "br i1 %jmp_inner, label %outer_loop, label %inner_loop, !llvm.loop !0\n"
161      << "end:\n"
162      << "ret void\n"
163      << "}\n"
164      << "!0 = distinct !{!0, !1, !2}\n"
165      << "!1 = !{!\"llvm.loop.vectorize.enable\", i1 true}\n"
166      << "!2 = !{!\"llvm.loop.vectorize.width\", i32 " << kArraySize << "}\n";
167 }
SingleLoopToString(std::ostream & os,const LoopFunction & x)168 void SingleLoopToString(std::ostream &os, const LoopFunction &x) {
169   os << "target triple = \"x86_64-unknown-linux-gnu\"\n"
170      << "define void @foo(i32* %a, i32* %b, i32* noalias %c, i64 %s) {\n"
171      << "%cmp = icmp sgt i64 %s, 0\n"
172      << "br i1 %cmp, label %start, label %end\n"
173      << "start:\n"
174      << "br label %loop\n"
175      << "end:\n"
176      << "ret void\n"
177      << "loop:\n"
178      << "%outer_ct = phi i64 [ %ctnew, %loop ], [ 0, %start ]\n"
179      << x.outer_statements()
180      << "%ctnew = add i64 %outer_ct, 1\n"
181      << "%j = icmp eq i64 %ctnew, %s\n"
182      << "br i1 %j, label %end, label %loop, !llvm.loop !0\n}\n"
183      << "!0 = distinct !{!0, !1, !2}\n"
184      << "!1 = !{!\"llvm.loop.vectorize.enable\", i1 true}\n"
185      << "!2 = !{!\"llvm.loop.vectorize.width\", i32 " << kArraySize << "}\n";
186 }
operator <<(std::ostream & os,const LoopFunction & x)187 std::ostream &operator<<(std::ostream &os, const LoopFunction &x) {
188   if (x.has_inner_statements())
189     NestedLoopToString(os, x);
190   else
191     SingleLoopToString(os, x);
192   return os;
193 }
194 
195 // ---------------------------------
196 
LoopFunctionToLLVMString(const LoopFunction & input)197 std::string LoopFunctionToLLVMString(const LoopFunction &input) {
198   std::ostringstream os;
199   os << input;
200   return os.str();
201 }
LoopProtoToLLVM(const uint8_t * data,size_t size)202 std::string LoopProtoToLLVM(const uint8_t *data, size_t size) {
203   LoopFunction message;
204   if (!message.ParsePartialFromArray(data, size))
205     return "#error invalid proto\n";
206   return LoopFunctionToLLVMString(message);
207 }
208 
209 } // namespace clang_fuzzer
210