transformation_add_synonym.cpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339
  1. // Copyright (c) 2020 Vasyl Teliman
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. #include "source/fuzz/transformation_add_synonym.h"
  15. #include <utility>
  16. #include "source/fuzz/fuzzer_util.h"
  17. #include "source/fuzz/instruction_descriptor.h"
  18. namespace spvtools {
  19. namespace fuzz {
  20. TransformationAddSynonym::TransformationAddSynonym(
  21. protobufs::TransformationAddSynonym message)
  22. : message_(std::move(message)) {}
  23. TransformationAddSynonym::TransformationAddSynonym(
  24. uint32_t result_id,
  25. protobufs::TransformationAddSynonym::SynonymType synonym_type,
  26. uint32_t synonym_fresh_id,
  27. const protobufs::InstructionDescriptor& insert_before) {
  28. message_.set_result_id(result_id);
  29. message_.set_synonym_type(synonym_type);
  30. message_.set_synonym_fresh_id(synonym_fresh_id);
  31. *message_.mutable_insert_before() = insert_before;
  32. }
  33. bool TransformationAddSynonym::IsApplicable(
  34. opt::IRContext* ir_context,
  35. const TransformationContext& transformation_context) const {
  36. assert(protobufs::TransformationAddSynonym::SynonymType_IsValid(
  37. message_.synonym_type()) &&
  38. "Synonym type is invalid");
  39. // |synonym_fresh_id| must be fresh.
  40. if (!fuzzerutil::IsFreshId(ir_context, message_.synonym_fresh_id())) {
  41. return false;
  42. }
  43. // Check that |message_.result_id| is valid.
  44. auto* synonym = ir_context->get_def_use_mgr()->GetDef(message_.result_id());
  45. if (!synonym) {
  46. return false;
  47. }
  48. // Check that we can apply |synonym_type| to |result_id|.
  49. if (!IsInstructionValid(ir_context, transformation_context, synonym,
  50. message_.synonym_type())) {
  51. return false;
  52. }
  53. // Check that |insert_before| is valid.
  54. auto* insert_before_inst =
  55. FindInstruction(message_.insert_before(), ir_context);
  56. if (!insert_before_inst) {
  57. return false;
  58. }
  59. const auto* insert_before_inst_block =
  60. ir_context->get_instr_block(insert_before_inst);
  61. assert(insert_before_inst_block &&
  62. "|insert_before_inst| must be in some block");
  63. if (transformation_context.GetFactManager()->BlockIsDead(
  64. insert_before_inst_block->id())) {
  65. // We don't create synonyms in dead blocks.
  66. return false;
  67. }
  68. // Check that we can insert |message._synonymous_instruction| before
  69. // |message_.insert_before| instruction. We use OpIAdd to represent some
  70. // instruction that can produce a synonym.
  71. if (!fuzzerutil::CanInsertOpcodeBeforeInstruction(spv::Op::OpIAdd,
  72. insert_before_inst)) {
  73. return false;
  74. }
  75. // A constant instruction must be present in the module if required.
  76. if (IsAdditionalConstantRequired(message_.synonym_type()) &&
  77. MaybeGetConstantId(ir_context, transformation_context) == 0) {
  78. return false;
  79. }
  80. // Domination rules must be satisfied.
  81. return fuzzerutil::IdIsAvailableBeforeInstruction(
  82. ir_context, insert_before_inst, message_.result_id());
  83. }
  84. void TransformationAddSynonym::Apply(
  85. opt::IRContext* ir_context,
  86. TransformationContext* transformation_context) const {
  87. // Add a synonymous instruction.
  88. auto new_instruction =
  89. MakeSynonymousInstruction(ir_context, *transformation_context);
  90. auto new_instruction_ptr = new_instruction.get();
  91. auto insert_before = FindInstruction(message_.insert_before(), ir_context);
  92. insert_before->InsertBefore(std::move(new_instruction));
  93. fuzzerutil::UpdateModuleIdBound(ir_context, message_.synonym_fresh_id());
  94. // Inform the def-use manager about the new instruction and record its basic
  95. // block.
  96. ir_context->get_def_use_mgr()->AnalyzeInstDefUse(new_instruction_ptr);
  97. ir_context->set_instr_block(new_instruction_ptr,
  98. ir_context->get_instr_block(insert_before));
  99. // Propagate PointeeValueIsIrrelevant fact.
  100. const auto* new_synonym_type = ir_context->get_type_mgr()->GetType(
  101. fuzzerutil::GetTypeId(ir_context, message_.synonym_fresh_id()));
  102. assert(new_synonym_type && "New synonym should have a valid type");
  103. if (transformation_context->GetFactManager()->PointeeValueIsIrrelevant(
  104. message_.result_id()) &&
  105. new_synonym_type->AsPointer()) {
  106. transformation_context->GetFactManager()->AddFactValueOfPointeeIsIrrelevant(
  107. message_.synonym_fresh_id());
  108. }
  109. // Mark two ids as synonymous.
  110. transformation_context->GetFactManager()->AddFactDataSynonym(
  111. MakeDataDescriptor(message_.result_id(), {}),
  112. MakeDataDescriptor(message_.synonym_fresh_id(), {}));
  113. }
  114. protobufs::Transformation TransformationAddSynonym::ToMessage() const {
  115. protobufs::Transformation result;
  116. *result.mutable_add_synonym() = message_;
  117. return result;
  118. }
  119. bool TransformationAddSynonym::IsInstructionValid(
  120. opt::IRContext* ir_context,
  121. const TransformationContext& transformation_context, opt::Instruction* inst,
  122. protobufs::TransformationAddSynonym::SynonymType synonym_type) {
  123. // Instruction must have a result id, type id. We skip OpUndef and
  124. // OpConstantNull.
  125. if (!inst || !inst->result_id() || !inst->type_id() ||
  126. inst->opcode() == spv::Op::OpUndef ||
  127. inst->opcode() == spv::Op::OpConstantNull) {
  128. return false;
  129. }
  130. if (!fuzzerutil::CanMakeSynonymOf(ir_context, transformation_context,
  131. *inst)) {
  132. return false;
  133. }
  134. switch (synonym_type) {
  135. case protobufs::TransformationAddSynonym::ADD_ZERO:
  136. case protobufs::TransformationAddSynonym::SUB_ZERO:
  137. case protobufs::TransformationAddSynonym::MUL_ONE: {
  138. // The instruction must be either scalar or vector of integers or floats.
  139. const auto* type = ir_context->get_type_mgr()->GetType(inst->type_id());
  140. assert(type && "Instruction's result id is invalid");
  141. if (const auto* vector = type->AsVector()) {
  142. return vector->element_type()->AsInteger() ||
  143. vector->element_type()->AsFloat();
  144. }
  145. return type->AsInteger() || type->AsFloat();
  146. }
  147. case protobufs::TransformationAddSynonym::BITWISE_OR:
  148. case protobufs::TransformationAddSynonym::BITWISE_XOR: {
  149. // The instruction must be either an integer or a vector of integers.
  150. const auto* type = ir_context->get_type_mgr()->GetType(inst->type_id());
  151. assert(type && "Instruction's result id is invalid");
  152. if (const auto* vector = type->AsVector()) {
  153. return vector->element_type()->AsInteger();
  154. }
  155. return type->AsInteger();
  156. }
  157. case protobufs::TransformationAddSynonym::COPY_OBJECT:
  158. // All checks for OpCopyObject are handled by
  159. // fuzzerutil::CanMakeSynonymOf.
  160. return true;
  161. case protobufs::TransformationAddSynonym::LOGICAL_AND:
  162. case protobufs::TransformationAddSynonym::LOGICAL_OR: {
  163. // The instruction must be either a scalar or a vector of booleans.
  164. const auto* type = ir_context->get_type_mgr()->GetType(inst->type_id());
  165. assert(type && "Instruction's result id is invalid");
  166. return (type->AsVector() && type->AsVector()->element_type()->AsBool()) ||
  167. type->AsBool();
  168. }
  169. default:
  170. assert(false && "Synonym type is not supported");
  171. return false;
  172. }
  173. }
  174. std::unique_ptr<opt::Instruction>
  175. TransformationAddSynonym::MakeSynonymousInstruction(
  176. opt::IRContext* ir_context,
  177. const TransformationContext& transformation_context) const {
  178. auto synonym_type_id =
  179. fuzzerutil::GetTypeId(ir_context, message_.result_id());
  180. assert(synonym_type_id && "Synonym has invalid type id");
  181. auto opcode = spv::Op::OpNop;
  182. const auto* synonym_type =
  183. ir_context->get_type_mgr()->GetType(synonym_type_id);
  184. assert(synonym_type && "Synonym has invalid type");
  185. auto is_integral = (synonym_type->AsVector() &&
  186. synonym_type->AsVector()->element_type()->AsInteger()) ||
  187. synonym_type->AsInteger();
  188. switch (message_.synonym_type()) {
  189. case protobufs::TransformationAddSynonym::SUB_ZERO:
  190. opcode = is_integral ? spv::Op::OpISub : spv::Op::OpFSub;
  191. break;
  192. case protobufs::TransformationAddSynonym::MUL_ONE:
  193. opcode = is_integral ? spv::Op::OpIMul : spv::Op::OpFMul;
  194. break;
  195. case protobufs::TransformationAddSynonym::ADD_ZERO:
  196. opcode = is_integral ? spv::Op::OpIAdd : spv::Op::OpFAdd;
  197. break;
  198. case protobufs::TransformationAddSynonym::LOGICAL_OR:
  199. opcode = spv::Op::OpLogicalOr;
  200. break;
  201. case protobufs::TransformationAddSynonym::LOGICAL_AND:
  202. opcode = spv::Op::OpLogicalAnd;
  203. break;
  204. case protobufs::TransformationAddSynonym::BITWISE_OR:
  205. opcode = spv::Op::OpBitwiseOr;
  206. break;
  207. case protobufs::TransformationAddSynonym::BITWISE_XOR:
  208. opcode = spv::Op::OpBitwiseXor;
  209. break;
  210. case protobufs::TransformationAddSynonym::COPY_OBJECT:
  211. return MakeUnique<opt::Instruction>(
  212. ir_context, spv::Op::OpCopyObject, synonym_type_id,
  213. message_.synonym_fresh_id(),
  214. opt::Instruction::OperandList{
  215. {SPV_OPERAND_TYPE_ID, {message_.result_id()}}});
  216. default:
  217. assert(false && "Unhandled synonym type");
  218. return nullptr;
  219. }
  220. return MakeUnique<opt::Instruction>(
  221. ir_context, opcode, synonym_type_id, message_.synonym_fresh_id(),
  222. opt::Instruction::OperandList{
  223. {SPV_OPERAND_TYPE_ID, {message_.result_id()}},
  224. {SPV_OPERAND_TYPE_ID,
  225. {MaybeGetConstantId(ir_context, transformation_context)}}});
  226. }
  227. uint32_t TransformationAddSynonym::MaybeGetConstantId(
  228. opt::IRContext* ir_context,
  229. const TransformationContext& transformation_context) const {
  230. assert(IsAdditionalConstantRequired(message_.synonym_type()) &&
  231. "Synonym type doesn't require an additional constant");
  232. auto synonym_type_id =
  233. fuzzerutil::GetTypeId(ir_context, message_.result_id());
  234. assert(synonym_type_id && "Synonym has invalid type id");
  235. switch (message_.synonym_type()) {
  236. case protobufs::TransformationAddSynonym::ADD_ZERO:
  237. case protobufs::TransformationAddSynonym::SUB_ZERO:
  238. case protobufs::TransformationAddSynonym::LOGICAL_OR:
  239. case protobufs::TransformationAddSynonym::BITWISE_OR:
  240. case protobufs::TransformationAddSynonym::BITWISE_XOR:
  241. return fuzzerutil::MaybeGetZeroConstant(
  242. ir_context, transformation_context, synonym_type_id, false);
  243. case protobufs::TransformationAddSynonym::MUL_ONE:
  244. case protobufs::TransformationAddSynonym::LOGICAL_AND: {
  245. auto synonym_type = ir_context->get_type_mgr()->GetType(synonym_type_id);
  246. assert(synonym_type && "Synonym has invalid type");
  247. if (const auto* vector = synonym_type->AsVector()) {
  248. auto element_type_id =
  249. ir_context->get_type_mgr()->GetId(vector->element_type());
  250. assert(element_type_id && "Vector's element type is invalid");
  251. auto one_word =
  252. vector->element_type()->AsFloat() ? fuzzerutil::FloatToWord(1) : 1u;
  253. if (auto scalar_one_id = fuzzerutil::MaybeGetScalarConstant(
  254. ir_context, transformation_context, {one_word}, element_type_id,
  255. false)) {
  256. return fuzzerutil::MaybeGetCompositeConstant(
  257. ir_context, transformation_context,
  258. std::vector<uint32_t>(vector->element_count(), scalar_one_id),
  259. synonym_type_id, false);
  260. }
  261. return 0;
  262. } else {
  263. return fuzzerutil::MaybeGetScalarConstant(
  264. ir_context, transformation_context,
  265. {synonym_type->AsFloat() ? fuzzerutil::FloatToWord(1) : 1u},
  266. synonym_type_id, false);
  267. }
  268. }
  269. default:
  270. // The assertion at the beginning of the function will fail in the debug
  271. // mode.
  272. return 0;
  273. }
  274. }
  275. bool TransformationAddSynonym::IsAdditionalConstantRequired(
  276. protobufs::TransformationAddSynonym::SynonymType synonym_type) {
  277. switch (synonym_type) {
  278. case protobufs::TransformationAddSynonym::ADD_ZERO:
  279. case protobufs::TransformationAddSynonym::SUB_ZERO:
  280. case protobufs::TransformationAddSynonym::LOGICAL_OR:
  281. case protobufs::TransformationAddSynonym::MUL_ONE:
  282. case protobufs::TransformationAddSynonym::LOGICAL_AND:
  283. case protobufs::TransformationAddSynonym::BITWISE_OR:
  284. case protobufs::TransformationAddSynonym::BITWISE_XOR:
  285. return true;
  286. default:
  287. return false;
  288. }
  289. }
  290. std::unordered_set<uint32_t> TransformationAddSynonym::GetFreshIds() const {
  291. return {message_.synonym_fresh_id()};
  292. }
  293. } // namespace fuzz
  294. } // namespace spvtools