2016-08-09 20:02:21 +00:00
|
|
|
// Copyright (c) 2016 Google Inc.
|
|
|
|
//
|
2016-09-01 19:33:59 +00:00
|
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
// you may not use this file except in compliance with the License.
|
|
|
|
// You may obtain a copy of the License at
|
2016-08-09 20:02:21 +00:00
|
|
|
//
|
2016-09-01 19:33:59 +00:00
|
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
2016-08-09 20:02:21 +00:00
|
|
|
//
|
2016-09-01 19:33:59 +00:00
|
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
// See the License for the specific language governing permissions and
|
|
|
|
// limitations under the License.
|
2016-08-09 20:02:21 +00:00
|
|
|
|
2018-08-03 12:05:33 +00:00
|
|
|
#ifndef SOURCE_OPT_PASS_H_
|
|
|
|
#define SOURCE_OPT_PASS_H_
|
2016-08-09 20:02:21 +00:00
|
|
|
|
2017-08-10 22:42:16 +00:00
|
|
|
#include <algorithm>
|
|
|
|
#include <map>
|
|
|
|
#include <unordered_map>
|
|
|
|
#include <unordered_set>
|
2016-09-21 21:16:31 +00:00
|
|
|
#include <utility>
|
2016-08-09 20:02:21 +00:00
|
|
|
|
2018-08-03 19:06:09 +00:00
|
|
|
#include "source/opt/basic_block.h"
|
|
|
|
#include "source/opt/def_use_manager.h"
|
|
|
|
#include "source/opt/ir_context.h"
|
|
|
|
#include "source/opt/module.h"
|
2016-09-21 21:16:31 +00:00
|
|
|
#include "spirv-tools/libspirv.hpp"
|
2019-08-29 16:48:17 +00:00
|
|
|
#include "types.h"
|
2016-08-09 20:02:21 +00:00
|
|
|
|
2022-03-23 02:50:52 +00:00
|
|
|
// Avoid unused variable warning/error on Linux
|
|
|
|
#ifndef NDEBUG
|
|
|
|
#define USE_ASSERT(x) assert(x)
|
|
|
|
#else
|
|
|
|
#define USE_ASSERT(x) ((void)(x))
|
|
|
|
#endif
|
|
|
|
|
2016-08-09 20:02:21 +00:00
|
|
|
namespace spvtools {
|
|
|
|
namespace opt {
|
|
|
|
|
|
|
|
// Abstract class of a pass. All passes should implement this abstract class
|
|
|
|
// and all analysis and transformation is done via the Process() method.
|
|
|
|
class Pass {
|
|
|
|
public:
|
2016-09-12 16:39:44 +00:00
|
|
|
// The status of processing a module using a pass.
|
|
|
|
//
|
|
|
|
// The numbers for the cases are assigned to make sure that Failure & anything
|
|
|
|
// is Failure, SuccessWithChange & any success is SuccessWithChange.
|
|
|
|
enum class Status {
|
|
|
|
Failure = 0x00,
|
|
|
|
SuccessWithChange = 0x10,
|
|
|
|
SuccessWithoutChange = 0x11,
|
|
|
|
};
|
|
|
|
|
2018-07-12 19:14:43 +00:00
|
|
|
using ProcessFunction = std::function<bool(Function*)>;
|
2017-08-10 22:42:16 +00:00
|
|
|
|
2017-02-28 19:34:18 +00:00
|
|
|
// Destructs the pass.
|
|
|
|
virtual ~Pass() = default;
|
|
|
|
|
2016-08-09 20:02:21 +00:00
|
|
|
// Returns a descriptive name for this pass.
|
2017-08-30 18:19:22 +00:00
|
|
|
//
|
|
|
|
// NOTE: When deriving a new pass class, make sure you make the name
|
|
|
|
// compatible with the corresponding spirv-opt command-line flag. For example,
|
|
|
|
// if you add the flag --my-pass to spirv-opt, make this function return
|
|
|
|
// "my-pass" (no leading hyphens).
|
2016-08-09 20:02:21 +00:00
|
|
|
virtual const char* name() const = 0;
|
2016-09-20 20:48:00 +00:00
|
|
|
|
|
|
|
// Sets the message consumer to the given |consumer|. |consumer| which will be
|
|
|
|
// invoked every time there is a message to be communicated to the outside.
|
|
|
|
void SetMessageConsumer(MessageConsumer c) { consumer_ = std::move(c); }
|
2017-10-25 17:26:25 +00:00
|
|
|
|
2016-08-20 03:17:44 +00:00
|
|
|
// Returns the reference to the message consumer for this pass.
|
|
|
|
const MessageConsumer& consumer() const { return consumer_; }
|
|
|
|
|
2017-10-25 17:26:25 +00:00
|
|
|
// Returns the def-use manager used for this pass. TODO(dnovillo): This should
|
|
|
|
// be handled by the pass manager.
|
|
|
|
analysis::DefUseManager* get_def_use_mgr() const {
|
2017-11-02 18:25:48 +00:00
|
|
|
return context()->get_def_use_mgr();
|
2017-10-25 17:26:25 +00:00
|
|
|
}
|
|
|
|
|
2017-11-13 20:31:43 +00:00
|
|
|
analysis::DecorationManager* get_decoration_mgr() const {
|
|
|
|
return context()->get_decoration_mgr();
|
|
|
|
}
|
|
|
|
|
2018-02-17 19:55:54 +00:00
|
|
|
FeatureManager* get_feature_mgr() const {
|
|
|
|
return context()->get_feature_mgr();
|
|
|
|
}
|
|
|
|
|
2017-10-25 17:26:25 +00:00
|
|
|
// Returns a pointer to the current module for this pass.
|
2018-07-12 19:14:43 +00:00
|
|
|
Module* get_module() const { return context_->module(); }
|
2017-10-30 15:13:24 +00:00
|
|
|
|
2018-07-12 13:08:45 +00:00
|
|
|
// Sets the pointer to the current context for this pass.
|
|
|
|
void SetContextForTesting(IRContext* ctx) { context_ = ctx; }
|
|
|
|
|
2017-10-30 15:13:24 +00:00
|
|
|
// Returns a pointer to the current context for this pass.
|
2018-07-12 19:14:43 +00:00
|
|
|
IRContext* context() const { return context_; }
|
2017-10-25 17:26:25 +00:00
|
|
|
|
2017-11-27 19:20:32 +00:00
|
|
|
// Returns a pointer to the CFG for current module.
|
2018-07-12 19:14:43 +00:00
|
|
|
CFG* cfg() const { return context()->cfg(); }
|
2017-10-30 21:42:26 +00:00
|
|
|
|
2017-11-08 17:40:02 +00:00
|
|
|
// Run the pass on the given |module|. Returns Status::Failure if errors occur
|
2018-04-10 17:38:33 +00:00
|
|
|
// when processing. Returns the corresponding Status::Success if processing is
|
2017-11-02 18:25:48 +00:00
|
|
|
// successful to indicate whether changes are made to the module. If there
|
|
|
|
// were any changes it will also invalidate the analyses in the IRContext
|
|
|
|
// that are not preserved.
|
2018-04-10 17:38:33 +00:00
|
|
|
//
|
|
|
|
// It is an error if |Run| is called twice with the same instance of the pass.
|
|
|
|
// If this happens the return value will be |Failure|.
|
2018-07-12 19:14:43 +00:00
|
|
|
Status Run(IRContext* ctx);
|
2017-11-02 18:25:48 +00:00
|
|
|
|
|
|
|
// Returns the set of analyses that the pass is guaranteed to preserve.
|
2018-07-12 19:14:43 +00:00
|
|
|
virtual IRContext::Analysis GetPreservedAnalyses() {
|
|
|
|
return IRContext::kAnalysisNone;
|
2017-11-02 18:25:48 +00:00
|
|
|
}
|
2016-08-20 03:17:44 +00:00
|
|
|
|
SSA rewrite pass.
This pass replaces the load/store elimination passes. It implements the
SSA re-writing algorithm proposed in
Simple and Efficient Construction of Static Single Assignment Form.
Braun M., Buchwald S., Hack S., Leißa R., Mallon C., Zwinkau A. (2013)
In: Jhala R., De Bosschere K. (eds)
Compiler Construction. CC 2013.
Lecture Notes in Computer Science, vol 7791.
Springer, Berlin, Heidelberg
https://link.springer.com/chapter/10.1007/978-3-642-37051-9_6
In contrast to common eager algorithms based on dominance and dominance
frontier information, this algorithm works backwards from load operations.
When a target variable is loaded, it queries the variable's reaching
definition. If the reaching definition is unknown at the current location,
it searches backwards in the CFG, inserting Phi instructions at join points
in the CFG along the way until it finds the desired store instruction.
The algorithm avoids repeated lookups using memoization.
For reducible CFGs, which are a superset of the structured CFGs in SPIRV,
this algorithm is proven to produce minimal SSA. That is, it inserts the
minimal number of Phi instructions required to ensure the SSA property, but
some Phi instructions may be dead
(https://en.wikipedia.org/wiki/Static_single_assignment_form).
2018-02-22 21:18:29 +00:00
|
|
|
// Return type id for |ptrInst|'s pointee
|
2018-07-12 19:14:43 +00:00
|
|
|
uint32_t GetPointeeTypeId(const Instruction* ptrInst) const;
|
SSA rewrite pass.
This pass replaces the load/store elimination passes. It implements the
SSA re-writing algorithm proposed in
Simple and Efficient Construction of Static Single Assignment Form.
Braun M., Buchwald S., Hack S., Leißa R., Mallon C., Zwinkau A. (2013)
In: Jhala R., De Bosschere K. (eds)
Compiler Construction. CC 2013.
Lecture Notes in Computer Science, vol 7791.
Springer, Berlin, Heidelberg
https://link.springer.com/chapter/10.1007/978-3-642-37051-9_6
In contrast to common eager algorithms based on dominance and dominance
frontier information, this algorithm works backwards from load operations.
When a target variable is loaded, it queries the variable's reaching
definition. If the reaching definition is unknown at the current location,
it searches backwards in the CFG, inserting Phi instructions at join points
in the CFG along the way until it finds the desired store instruction.
The algorithm avoids repeated lookups using memoization.
For reducible CFGs, which are a superset of the structured CFGs in SPIRV,
this algorithm is proven to produce minimal SSA. That is, it inserts the
minimal number of Phi instructions required to ensure the SSA property, but
some Phi instructions may be dead
(https://en.wikipedia.org/wiki/Static_single_assignment_form).
2018-02-22 21:18:29 +00:00
|
|
|
|
2019-09-03 17:22:13 +00:00
|
|
|
// Return base type of |ty_id| type
|
|
|
|
Instruction* GetBaseType(uint32_t ty_id);
|
|
|
|
|
|
|
|
// Return true if |inst| returns scalar, vector or matrix type with base
|
|
|
|
// float and |width|
|
|
|
|
bool IsFloat(uint32_t ty_id, uint32_t width);
|
|
|
|
|
2019-09-03 19:19:36 +00:00
|
|
|
// Return the id of OpConstantNull of type |type_id|. Create if necessary.
|
|
|
|
uint32_t GetNullId(uint32_t type_id);
|
|
|
|
|
2017-10-25 17:26:25 +00:00
|
|
|
protected:
|
2018-07-12 13:08:45 +00:00
|
|
|
// Constructs a new pass.
|
|
|
|
//
|
|
|
|
// The constructed instance will have an empty message consumer, which just
|
|
|
|
// ignores all messages from the library. Use SetMessageConsumer() to supply
|
|
|
|
// one if messages are of concern.
|
|
|
|
Pass();
|
2017-10-25 17:26:25 +00:00
|
|
|
|
2017-11-02 18:25:48 +00:00
|
|
|
// Processes the given |module|. Returns Status::Failure if errors occur when
|
|
|
|
// processing. Returns the corresponding Status::Success if processing is
|
2022-01-26 20:13:08 +00:00
|
|
|
// successful to indicate whether changes are made to the module.
|
2018-07-12 13:08:45 +00:00
|
|
|
virtual Status Process() = 0;
|
2017-11-02 18:25:48 +00:00
|
|
|
|
2017-12-07 21:42:27 +00:00
|
|
|
// Return the next available SSA id and increment it.
|
2018-12-06 14:07:00 +00:00
|
|
|
// TODO(1841): Handle id overflow.
|
2017-12-07 21:42:27 +00:00
|
|
|
uint32_t TakeNextId() { return context_->TakeNextId(); }
|
2017-10-25 17:26:25 +00:00
|
|
|
|
2019-05-02 13:31:46 +00:00
|
|
|
// Returns the id whose value is the same as |object_to_copy| except its type
|
|
|
|
// is |new_type_id|. Any instructions needed to generate this value will be
|
|
|
|
// inserted before |insertion_position|.
|
|
|
|
uint32_t GenerateCopy(Instruction* object_to_copy, uint32_t new_type_id,
|
|
|
|
Instruction* insertion_position);
|
|
|
|
|
2016-08-20 03:17:44 +00:00
|
|
|
private:
|
2016-09-20 20:48:00 +00:00
|
|
|
MessageConsumer consumer_; // Message consumer.
|
2017-10-25 17:26:25 +00:00
|
|
|
|
2017-10-30 21:42:26 +00:00
|
|
|
// The context that this pass belongs to.
|
2018-07-12 19:14:43 +00:00
|
|
|
IRContext* context_;
|
2018-04-10 17:38:33 +00:00
|
|
|
|
|
|
|
// An instance of a pass can only be run once because it is too hard to
|
|
|
|
// enforce proper resetting of internal state for each instance. This member
|
|
|
|
// is used to check that we do not run the same instance twice.
|
|
|
|
bool already_run_;
|
2016-08-09 20:02:21 +00:00
|
|
|
};
|
|
|
|
|
2018-12-06 14:07:00 +00:00
|
|
|
inline Pass::Status CombineStatus(Pass::Status a, Pass::Status b) {
|
|
|
|
return std::min(a, b);
|
|
|
|
}
|
|
|
|
|
2016-08-09 20:02:21 +00:00
|
|
|
} // namespace opt
|
|
|
|
} // namespace spvtools
|
|
|
|
|
2018-08-03 12:05:33 +00:00
|
|
|
#endif // SOURCE_OPT_PASS_H_
|