1/* -*- mode: C; c-basic-offset: 3; -*- */
2
3/*---------------------------------------------------------------*/
4/*--- begin                                       ir_inject.c ---*/
5/*---------------------------------------------------------------*/
6
7
8/*
9   This file is part of Valgrind, a dynamic binary instrumentation
10   framework.
11
12   Copyright (C) 2012-2013  Florian Krohm   (britzel@acm.org)
13
14   This program is free software; you can redistribute it and/or
15   modify it under the terms of the GNU General Public License as
16   published by the Free Software Foundation; either version 2 of the
17   License, or (at your option) any later version.
18
19   This program is distributed in the hope that it will be useful, but
20   WITHOUT ANY WARRANTY; without even the implied warranty of
21   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
22   General Public License for more details.
23
24   You should have received a copy of the GNU General Public License
25   along with this program; if not, write to the Free Software
26   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
27   02110-1301, USA.
28
29   The GNU General Public License is contained in the file COPYING.
30*/
31
32#include "libvex_basictypes.h"
33#include "libvex_ir.h"
34#include "libvex.h"
35#include "main_util.h"
36
37/* Convenience macros for readibility */
38#define mkU8(v)   IRExpr_Const(IRConst_U8(v))
39#define mkU32(v)  IRExpr_Const(IRConst_U32(v))
40#define mkU64(v)  IRExpr_Const(IRConst_U64(v))
41#define unop(kind, a)  IRExpr_Unop(kind, a)
42#define binop(kind, a1, a2)  IRExpr_Binop(kind, a1, a2)
43#define triop(kind, a1, a2, a3)  IRExpr_Triop(kind, a1, a2, a3)
44#define qop(kind, a1, a2, a3, a4)  IRExpr_Qop(kind, a1, a2, a3, a4)
45#define stmt(irsb, st)  addStmtToIRSB(irsb, st)
46
47
48/* The IR Injection Control Block. vex_inject_ir will query its contents
49   to construct IR statements for testing purposes. */
50static IRICB iricb;
51
52
53void
54LibVEX_InitIRI(const IRICB *iricb_in)
55{
56   iricb = *iricb_in;  // copy in
57}
58
59
60static IRExpr *
61load_aux(IREndness endian, IRType type, IRExpr *addr)
62{
63   if (type == Ity_D64) {
64      /* The insn selectors do not support loading a DFP value from memory.
65         So we need to fix it here by loading an integer value and
66         reinterpreting it as DFP. */
67      return unop(Iop_ReinterpI64asD64,
68                  IRExpr_Load(endian, Ity_I64, addr));
69   }
70   if (type == Ity_I1) {
71      /* A Boolean value is stored as a 32-bit entity (see store_aux). */
72      return unop(Iop_32to1, IRExpr_Load(endian, Ity_I32, addr));
73   }
74
75   return IRExpr_Load(endian, type, addr);
76}
77
78
79/* Load a value from memory. Loads of more than 8 byte are split into
80   a series of 8-byte loads and combined using appropriate IROps. */
81static IRExpr *
82load(IREndness endian, IRType type, HWord haddr)
83{
84   IROp concat;
85   IRExpr *addr, *next_addr;
86
87   vassert(type == Ity_I1 || sizeofIRType(type) <= 16);
88
89   if (VEX_HOST_WORDSIZE == 8) {
90      addr = mkU64(haddr);
91      next_addr = binop(Iop_Add64, addr, mkU64(8));
92   } else if (VEX_HOST_WORDSIZE == 4) {
93      addr = mkU32(haddr);
94      next_addr = binop(Iop_Add32, addr, mkU32(8));
95   } else {
96      vpanic("invalid #bytes for address");
97   }
98
99   switch (type) {
100   case Ity_I128: concat = Iop_64HLto128;   type = Ity_I64; goto load128;
101   case Ity_F128: concat = Iop_F64HLtoF128; type = Ity_F64; goto load128;
102   case Ity_D128: concat = Iop_D64HLtoD128; type = Ity_D64; goto load128;
103
104   load128:
105     /* Two loads of 64 bit each. */
106      if (endian == Iend_BE) {
107         /* The more significant bits are at the lower address. */
108         return binop(concat,
109                      load_aux(endian, type, addr),
110                      load_aux(endian, type, next_addr));
111      } else {
112         /* The more significant bits are at the higher address. */
113         return binop(concat,
114                      load_aux(endian, type, next_addr),
115                      load_aux(endian, type, addr));
116      }
117
118   default:
119      return load_aux(endian, type, addr);
120   }
121}
122
123
124static void
125store_aux(IRSB *irsb, IREndness endian, IRExpr *addr, IRExpr *data)
126{
127   if (typeOfIRExpr(irsb->tyenv, data) == Ity_D64) {
128      /* The insn selectors do not support writing a DFP value to memory.
129         So we need to fix it here by reinterpreting the DFP value as an
130         integer and storing that. */
131      data = unop(Iop_ReinterpD64asI64, data);
132   }
133   if (typeOfIRExpr(irsb->tyenv, data) == Ity_I1) {
134      /* We cannot store a single bit. So we store it in a 32-bit container.
135         See also load_aux. */
136      data = unop(Iop_1Uto32, data);
137   }
138   stmt(irsb, IRStmt_Store(endian, addr, data));
139}
140
141
142/* Store a value to memory. If a value requires more than 8 bytes a series
143   of 8-byte stores will be generated. */
144static void __inline__
145store(IRSB *irsb, IREndness endian, HWord haddr, IRExpr *data)
146{
147   IROp high, low;
148   IRExpr *addr, *next_addr;
149
150   if (VEX_HOST_WORDSIZE == 8) {
151      addr = mkU64(haddr);
152      next_addr = binop(Iop_Add64, addr, mkU64(8));
153   } else if (VEX_HOST_WORDSIZE == 4) {
154      addr = mkU32(haddr);
155      next_addr = binop(Iop_Add32, addr, mkU32(8));
156   } else {
157      vpanic("invalid #bytes for address");
158   }
159
160   IRType type = typeOfIRExpr(irsb->tyenv, data);
161
162   vassert(type == Ity_I1 || sizeofIRType(type) <= 16);
163
164   switch (type) {
165   case Ity_I128: high = Iop_128HIto64;   low = Iop_128to64;     goto store128;
166   case Ity_F128: high = Iop_F128HItoF64; low = Iop_F128LOtoF64; goto store128;
167   case Ity_D128: high = Iop_D128HItoD64; low = Iop_D128LOtoD64; goto store128;
168
169   store128:
170     /* Two stores of 64 bit each. */
171      if (endian == Iend_BE) {
172         /* The more significant bits are at the lower address. */
173         store_aux(irsb, endian, addr, unop(high, data));
174         store_aux(irsb, endian, next_addr, unop(low, data));
175      } else {
176         /* The more significant bits are at the higher address. */
177         store_aux(irsb, endian, addr, unop(low, data));
178         store_aux(irsb, endian, next_addr, unop(high, data));
179      }
180      return;
181
182   default:
183      store_aux(irsb, endian, addr, data);
184      return;
185   }
186}
187
188
189/* Inject IR stmts depending on the data provided in the control
190   block iricb. */
191void
192vex_inject_ir(IRSB *irsb, IREndness endian)
193{
194   IRExpr *data, *rounding_mode, *opnd1, *opnd2, *opnd3, *opnd4;
195
196   rounding_mode = NULL;
197   if (iricb.rounding_mode != NO_ROUNDING_MODE) {
198      rounding_mode = mkU32(iricb.rounding_mode);
199   }
200
201   switch (iricb.num_operands) {
202   case 1:
203      opnd1 = load(endian, iricb.t_opnd1, iricb.opnd1);
204      if (rounding_mode)
205         data = binop(iricb.op, rounding_mode, opnd1);
206      else
207         data = unop(iricb.op, opnd1);
208      break;
209
210   case 2:
211      opnd1 = load(endian, iricb.t_opnd1, iricb.opnd1);
212
213      if (iricb.shift_amount_is_immediate) {
214         // This implies that the IROp is a shift op
215         vassert(iricb.t_opnd2 == Ity_I8);
216         opnd2 = mkU8(*((Char *)iricb.opnd2));
217      } else {
218         opnd2 = load(endian, iricb.t_opnd2, iricb.opnd2);
219      }
220
221      if (rounding_mode)
222         data = triop(iricb.op, rounding_mode, opnd1, opnd2);
223      else
224         data = binop(iricb.op, opnd1, opnd2);
225      break;
226
227   case 3:
228      opnd1 = load(endian, iricb.t_opnd1, iricb.opnd1);
229      opnd2 = load(endian, iricb.t_opnd2, iricb.opnd2);
230      opnd3 = load(endian, iricb.t_opnd3, iricb.opnd3);
231      if (rounding_mode)
232         data = qop(iricb.op, rounding_mode, opnd1, opnd2, opnd3);
233      else
234         data = triop(iricb.op, opnd1, opnd2, opnd3);
235      break;
236
237   case 4:
238      vassert(rounding_mode == NULL);
239      opnd1 = load(endian, iricb.t_opnd1, iricb.opnd1);
240      opnd2 = load(endian, iricb.t_opnd2, iricb.opnd2);
241      opnd3 = load(endian, iricb.t_opnd3, iricb.opnd3);
242      opnd4 = load(endian, iricb.t_opnd4, iricb.opnd4);
243      data = qop(iricb.op, opnd1, opnd2, opnd3, opnd4);
244      break;
245
246   default:
247      vpanic("unsupported operator");
248   }
249
250   store(irsb, endian, iricb.result, data);
251
252   if (0) {
253      vex_printf("BEGIN inject\n");
254      if (iricb.t_result == Ity_I1 || sizeofIRType(iricb.t_result) <= 8) {
255         ppIRStmt(irsb->stmts[irsb->stmts_used - 1]);
256      } else if (sizeofIRType(iricb.t_result) == 16) {
257         ppIRStmt(irsb->stmts[irsb->stmts_used - 2]);
258         vex_printf("\n");
259         ppIRStmt(irsb->stmts[irsb->stmts_used - 1]);
260      }
261      vex_printf("\nEND inject\n");
262   }
263}
264
265/*---------------------------------------------------------------*/
266/*--- end                                         ir_inject.c ---*/
267/*---------------------------------------------------------------*/
268