transformation_add_synonym.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328
  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(SpvOpIAdd,
  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. FindInstruction(message_.insert_before(), ir_context)
  89. ->InsertBefore(
  90. MakeSynonymousInstruction(ir_context, *transformation_context));
  91. fuzzerutil::UpdateModuleIdBound(ir_context, message_.synonym_fresh_id());
  92. ir_context->InvalidateAnalysesExceptFor(
  93. opt::IRContext::Analysis::kAnalysisNone);
  94. // Propagate PointeeValueIsIrrelevant fact.
  95. const auto* new_synonym_type = ir_context->get_type_mgr()->GetType(
  96. fuzzerutil::GetTypeId(ir_context, message_.synonym_fresh_id()));
  97. assert(new_synonym_type && "New synonym should have a valid type");
  98. if (transformation_context->GetFactManager()->PointeeValueIsIrrelevant(
  99. message_.result_id()) &&
  100. new_synonym_type->AsPointer()) {
  101. transformation_context->GetFactManager()->AddFactValueOfPointeeIsIrrelevant(
  102. message_.synonym_fresh_id());
  103. }
  104. // Mark two ids as synonymous.
  105. transformation_context->GetFactManager()->AddFactDataSynonym(
  106. MakeDataDescriptor(message_.result_id(), {}),
  107. MakeDataDescriptor(message_.synonym_fresh_id(), {}));
  108. }
  109. protobufs::Transformation TransformationAddSynonym::ToMessage() const {
  110. protobufs::Transformation result;
  111. *result.mutable_add_synonym() = message_;
  112. return result;
  113. }
  114. bool TransformationAddSynonym::IsInstructionValid(
  115. opt::IRContext* ir_context,
  116. const TransformationContext& transformation_context, opt::Instruction* inst,
  117. protobufs::TransformationAddSynonym::SynonymType synonym_type) {
  118. // Instruction must have a result id, type id. We skip OpUndef and
  119. // OpConstantNull.
  120. if (!inst || !inst->result_id() || !inst->type_id() ||
  121. inst->opcode() == SpvOpUndef || inst->opcode() == SpvOpConstantNull) {
  122. return false;
  123. }
  124. if (!fuzzerutil::CanMakeSynonymOf(ir_context, transformation_context, inst)) {
  125. return false;
  126. }
  127. switch (synonym_type) {
  128. case protobufs::TransformationAddSynonym::ADD_ZERO:
  129. case protobufs::TransformationAddSynonym::SUB_ZERO:
  130. case protobufs::TransformationAddSynonym::MUL_ONE: {
  131. // The instruction must be either scalar or vector of integers or floats.
  132. const auto* type = ir_context->get_type_mgr()->GetType(inst->type_id());
  133. assert(type && "Instruction's result id is invalid");
  134. if (const auto* vector = type->AsVector()) {
  135. return vector->element_type()->AsInteger() ||
  136. vector->element_type()->AsFloat();
  137. }
  138. return type->AsInteger() || type->AsFloat();
  139. }
  140. case protobufs::TransformationAddSynonym::COPY_OBJECT:
  141. // All checks for OpCopyObject are handled by
  142. // fuzzerutil::CanMakeSynonymOf.
  143. return true;
  144. case protobufs::TransformationAddSynonym::LOGICAL_AND:
  145. case protobufs::TransformationAddSynonym::LOGICAL_OR: {
  146. // The instruction must be either a scalar or a vector of booleans.
  147. const auto* type = ir_context->get_type_mgr()->GetType(inst->type_id());
  148. assert(type && "Instruction's result id is invalid");
  149. return (type->AsVector() && type->AsVector()->element_type()->AsBool()) ||
  150. type->AsBool();
  151. }
  152. default:
  153. assert(false && "Synonym type is not supported");
  154. return false;
  155. }
  156. }
  157. std::unique_ptr<opt::Instruction>
  158. TransformationAddSynonym::MakeSynonymousInstruction(
  159. opt::IRContext* ir_context,
  160. const TransformationContext& transformation_context) const {
  161. auto synonym_type_id =
  162. fuzzerutil::GetTypeId(ir_context, message_.result_id());
  163. assert(synonym_type_id && "Synonym has invalid type id");
  164. switch (message_.synonym_type()) {
  165. case protobufs::TransformationAddSynonym::SUB_ZERO:
  166. case protobufs::TransformationAddSynonym::MUL_ONE:
  167. case protobufs::TransformationAddSynonym::ADD_ZERO: {
  168. const auto* synonym_type =
  169. ir_context->get_type_mgr()->GetType(synonym_type_id);
  170. assert(synonym_type && "Synonym has invalid type");
  171. // Compute instruction's opcode based on the type of the operand.
  172. // We have already checked that the operand is either a scalar or a vector
  173. // of either integers or floats.
  174. auto is_integral =
  175. (synonym_type->AsVector() &&
  176. synonym_type->AsVector()->element_type()->AsInteger()) ||
  177. synonym_type->AsInteger();
  178. auto opcode = SpvOpNop;
  179. switch (message_.synonym_type()) {
  180. case protobufs::TransformationAddSynonym::SUB_ZERO:
  181. opcode = is_integral ? SpvOpISub : SpvOpFSub;
  182. break;
  183. case protobufs::TransformationAddSynonym::MUL_ONE:
  184. opcode = is_integral ? SpvOpIMul : SpvOpFMul;
  185. break;
  186. case protobufs::TransformationAddSynonym::ADD_ZERO:
  187. opcode = is_integral ? SpvOpIAdd : SpvOpFAdd;
  188. break;
  189. default:
  190. assert(false && "Unreachable");
  191. break;
  192. }
  193. return MakeUnique<opt::Instruction>(
  194. ir_context, opcode, synonym_type_id, message_.synonym_fresh_id(),
  195. opt::Instruction::OperandList{
  196. {SPV_OPERAND_TYPE_ID, {message_.result_id()}},
  197. {SPV_OPERAND_TYPE_ID,
  198. {MaybeGetConstantId(ir_context, transformation_context)}}});
  199. }
  200. case protobufs::TransformationAddSynonym::COPY_OBJECT:
  201. return MakeUnique<opt::Instruction>(
  202. ir_context, SpvOpCopyObject, synonym_type_id,
  203. message_.synonym_fresh_id(),
  204. opt::Instruction::OperandList{
  205. {SPV_OPERAND_TYPE_ID, {message_.result_id()}}});
  206. case protobufs::TransformationAddSynonym::LOGICAL_OR:
  207. case protobufs::TransformationAddSynonym::LOGICAL_AND: {
  208. auto opcode = message_.synonym_type() ==
  209. protobufs::TransformationAddSynonym::LOGICAL_OR
  210. ? SpvOpLogicalOr
  211. : SpvOpLogicalAnd;
  212. return MakeUnique<opt::Instruction>(
  213. ir_context, opcode, synonym_type_id, message_.synonym_fresh_id(),
  214. opt::Instruction::OperandList{
  215. {SPV_OPERAND_TYPE_ID, {message_.result_id()}},
  216. {SPV_OPERAND_TYPE_ID,
  217. {MaybeGetConstantId(ir_context, transformation_context)}}});
  218. }
  219. default:
  220. assert(false && "Unhandled synonym type");
  221. return nullptr;
  222. }
  223. }
  224. uint32_t TransformationAddSynonym::MaybeGetConstantId(
  225. opt::IRContext* ir_context,
  226. const TransformationContext& transformation_context) const {
  227. assert(IsAdditionalConstantRequired(message_.synonym_type()) &&
  228. "Synonym type doesn't require an additional constant");
  229. auto synonym_type_id =
  230. fuzzerutil::GetTypeId(ir_context, message_.result_id());
  231. assert(synonym_type_id && "Synonym has invalid type id");
  232. switch (message_.synonym_type()) {
  233. case protobufs::TransformationAddSynonym::ADD_ZERO:
  234. case protobufs::TransformationAddSynonym::SUB_ZERO:
  235. case protobufs::TransformationAddSynonym::LOGICAL_OR:
  236. return fuzzerutil::MaybeGetZeroConstant(
  237. ir_context, transformation_context, synonym_type_id, false);
  238. case protobufs::TransformationAddSynonym::MUL_ONE:
  239. case protobufs::TransformationAddSynonym::LOGICAL_AND: {
  240. auto synonym_type = ir_context->get_type_mgr()->GetType(synonym_type_id);
  241. assert(synonym_type && "Synonym has invalid type");
  242. if (const auto* vector = synonym_type->AsVector()) {
  243. auto element_type_id =
  244. ir_context->get_type_mgr()->GetId(vector->element_type());
  245. assert(element_type_id && "Vector's element type is invalid");
  246. auto one_word =
  247. vector->element_type()->AsFloat() ? fuzzerutil::FloatToWord(1) : 1u;
  248. if (auto scalar_one_id = fuzzerutil::MaybeGetScalarConstant(
  249. ir_context, transformation_context, {one_word}, element_type_id,
  250. false)) {
  251. return fuzzerutil::MaybeGetCompositeConstant(
  252. ir_context, transformation_context,
  253. std::vector<uint32_t>(vector->element_count(), scalar_one_id),
  254. synonym_type_id, false);
  255. }
  256. return 0;
  257. } else {
  258. return fuzzerutil::MaybeGetScalarConstant(
  259. ir_context, transformation_context,
  260. {synonym_type->AsFloat() ? fuzzerutil::FloatToWord(1) : 1u},
  261. synonym_type_id, false);
  262. }
  263. }
  264. default:
  265. // The assertion at the beginning of the function will fail in the debug
  266. // mode.
  267. return 0;
  268. }
  269. }
  270. bool TransformationAddSynonym::IsAdditionalConstantRequired(
  271. protobufs::TransformationAddSynonym::SynonymType synonym_type) {
  272. switch (synonym_type) {
  273. case protobufs::TransformationAddSynonym::ADD_ZERO:
  274. case protobufs::TransformationAddSynonym::SUB_ZERO:
  275. case protobufs::TransformationAddSynonym::LOGICAL_OR:
  276. case protobufs::TransformationAddSynonym::MUL_ONE:
  277. case protobufs::TransformationAddSynonym::LOGICAL_AND:
  278. return true;
  279. default:
  280. return false;
  281. }
  282. }
  283. std::unordered_set<uint32_t> TransformationAddSynonym::GetFreshIds() const {
  284. return {message_.synonym_fresh_id()};
  285. }
  286. } // namespace fuzz
  287. } // namespace spvtools