Commit 6328c565 authored by Tobias Tebbi's avatar Tobias Tebbi Committed by Commit Bot

Reland "[turbofan] add value input to DeadValue"

DeadValue was a constant node of type None. This is unsound in the
presence of re-scheduling. This CL adds a value input to DeadValue,
which preserves the dependency on the original node of type None.

This reland addresses the bug that the EffectControlLinearizer could destroy dependencies of DeadValue by attaching DeadValue nodes to the effect chain in the EffectControlLinearizer.

Bug: chromium:796041 chromium:798938
Change-Id: If47b54a7986d257eb63b437f855769b503679ff5
Reviewed-on: https://chromium-review.googlesource.com/850392Reviewed-by: 's avatarJaroslav Sevcik <jarin@chromium.org>
Commit-Queue: Tobias Tebbi <tebbi@chromium.org>
Cr-Commit-Position: refs/heads/master@{#50360}
parent 6b050c03
......@@ -348,8 +348,7 @@ ZoneVector<MachineType> const* MachineTypesOf(Operator const* op) {
#define COMMON_CACHED_OP_LIST(V) \
V(Dead, Operator::kFoldable, 0, 0, 0, 1, 1, 1) \
V(DeadValue, Operator::kFoldable, 0, 0, 0, 1, 0, 0) \
V(Unreachable, Operator::kFoldable, 0, 1, 1, 0, 1, 0) \
V(Unreachable, Operator::kFoldable, 0, 1, 1, 1, 1, 0) \
V(IfTrue, Operator::kKontrol, 0, 0, 1, 0, 0, 1) \
V(IfFalse, Operator::kKontrol, 0, 0, 1, 0, 0, 1) \
V(IfSuccess, Operator::kKontrol, 0, 0, 1, 0, 0, 1) \
......@@ -1286,6 +1285,11 @@ uint32_t ObjectIdOf(Operator const* op) {
}
}
MachineRepresentation DeadValueRepresentationOf(Operator const* op) {
DCHECK_EQ(IrOpcode::kDeadValue, op->opcode());
return OpParameter<MachineRepresentation>(op);
}
const Operator* CommonOperatorBuilder::FrameState(
BailoutId bailout_id, OutputFrameStateCombine state_combine,
const FrameStateFunctionInfo* function_info) {
......@@ -1401,6 +1405,14 @@ CommonOperatorBuilder::CreateFrameStateFunctionInfo(
FrameStateFunctionInfo(type, parameter_count, local_count, shared_info);
}
const Operator* CommonOperatorBuilder::DeadValue(MachineRepresentation rep) {
return new (zone()) Operator1<MachineRepresentation>( // --
IrOpcode::kDeadValue, Operator::kPure, // opcode
"DeadValue", // name
1, 0, 0, 1, 0, 0, // counts
rep); // parameter
}
#undef COMMON_CACHED_OP_LIST
#undef CACHED_RETURN_LIST
#undef CACHED_END_LIST
......
......@@ -342,6 +342,8 @@ ArgumentsStateType ArgumentsStateTypeOf(Operator const*) WARN_UNUSED_RESULT;
uint32_t ObjectIdOf(Operator const*);
MachineRepresentation DeadValueRepresentationOf(Operator const*);
// Interface for building common operators that can be used at any level of IR,
// including JavaScript, mid-level, and low-level.
class V8_EXPORT_PRIVATE CommonOperatorBuilder final
......@@ -350,7 +352,7 @@ class V8_EXPORT_PRIVATE CommonOperatorBuilder final
explicit CommonOperatorBuilder(Zone* zone);
const Operator* Dead();
const Operator* DeadValue();
const Operator* DeadValue(MachineRepresentation rep);
const Operator* Unreachable();
const Operator* End(size_t control_input_count);
const Operator* Branch(BranchHint = BranchHint::kNone);
......
......@@ -21,10 +21,8 @@ DeadCodeElimination::DeadCodeElimination(Editor* editor, Graph* graph,
graph_(graph),
common_(common),
dead_(graph->NewNode(common->Dead())),
dead_value_(graph->NewNode(common->DeadValue())),
zone_(temp_zone) {
NodeProperties::SetType(dead_, Type::None());
NodeProperties::SetType(dead_value_, Type::None());
}
namespace {
......@@ -38,11 +36,11 @@ bool NoReturn(Node* node) {
NodeProperties::GetTypeOrAny(node)->IsNone();
}
bool HasDeadInput(Node* node) {
Node* FindDeadInput(Node* node) {
for (Node* input : node->inputs()) {
if (NoReturn(input)) return true;
if (NoReturn(input)) return input;
}
return false;
return nullptr;
}
} // namespace
......@@ -209,17 +207,27 @@ Reduction DeadCodeElimination::ReducePhi(Node* node) {
DCHECK_EQ(IrOpcode::kPhi, node->opcode());
Reduction reduction = PropagateDeadControl(node);
if (reduction.Changed()) return reduction;
if (PhiRepresentationOf(node->op()) == MachineRepresentation::kNone ||
MachineRepresentation rep = PhiRepresentationOf(node->op());
if (rep == MachineRepresentation::kNone ||
NodeProperties::GetTypeOrAny(node)->IsNone()) {
return Replace(dead_value());
return Replace(DeadValue(node, rep));
}
int input_count = node->op()->ValueInputCount();
for (int i = 0; i < input_count; ++i) {
Node* input = NodeProperties::GetValueInput(node, i);
if (input->opcode() == IrOpcode::kDeadValue &&
DeadValueRepresentationOf(input->op()) != rep) {
NodeProperties::ReplaceValueInput(node, DeadValue(input, rep), i);
}
}
return NoChange();
}
Reduction DeadCodeElimination::ReducePureNode(Node* node) {
DCHECK_EQ(0, node->op()->EffectInputCount());
if (HasDeadInput(node)) {
return Replace(dead_value());
if (node->opcode() == IrOpcode::kDeadValue) return NoChange();
if (Node* input = FindDeadInput(node)) {
return Replace(DeadValue(input));
}
return NoChange();
}
......@@ -234,8 +242,7 @@ Reduction DeadCodeElimination::ReduceUnreachableOrIfException(Node* node) {
return Replace(effect);
}
if (effect->opcode() == IrOpcode::kUnreachable) {
RelaxEffectsAndControls(node);
return Replace(dead_value());
return Replace(effect);
}
return NoChange();
}
......@@ -246,10 +253,10 @@ Reduction DeadCodeElimination::ReduceEffectNode(Node* node) {
if (effect->opcode() == IrOpcode::kDead) {
return Replace(effect);
}
if (HasDeadInput(node)) {
if (Node* input = FindDeadInput(node)) {
if (effect->opcode() == IrOpcode::kUnreachable) {
RelaxEffectsAndControls(node);
return Replace(dead_value());
return Replace(DeadValue(input));
}
Node* control = node->op()->ControlInputCount() == 1
......@@ -257,7 +264,8 @@ Reduction DeadCodeElimination::ReduceEffectNode(Node* node) {
: graph()->start();
Node* unreachable =
graph()->NewNode(common()->Unreachable(), effect, control);
ReplaceWithValue(node, dead_value(), node, control);
NodeProperties::SetType(unreachable, Type::None());
ReplaceWithValue(node, DeadValue(input), node, control);
return Replace(unreachable);
}
......@@ -270,11 +278,12 @@ Reduction DeadCodeElimination::ReduceDeoptimizeOrReturnOrTerminate(Node* node) {
node->opcode() == IrOpcode::kTerminate);
Reduction reduction = PropagateDeadControl(node);
if (reduction.Changed()) return reduction;
if (HasDeadInput(node)) {
if (FindDeadInput(node) != nullptr) {
Node* effect = NodeProperties::GetEffectInput(node, 0);
Node* control = NodeProperties::GetControlInput(node, 0);
if (effect->opcode() != IrOpcode::kUnreachable) {
effect = graph()->NewNode(common()->Unreachable(), effect, control);
NodeProperties::SetType(effect, Type::None());
}
node->TrimInputCount(2);
node->ReplaceInput(0, effect);
......@@ -322,6 +331,16 @@ void DeadCodeElimination::TrimMergeOrPhi(Node* node, int size) {
NodeProperties::ChangeOp(node, op);
}
Node* DeadCodeElimination::DeadValue(Node* node, MachineRepresentation rep) {
if (node->opcode() == IrOpcode::kDeadValue) {
if (rep == DeadValueRepresentationOf(node->op())) return node;
node = NodeProperties::GetValueInput(node, 0);
}
Node* dead_value = graph()->NewNode(common()->DeadValue(rep), node);
NodeProperties::SetType(dead_value, Type::None());
return dead_value;
}
} // namespace compiler
} // namespace internal
} // namespace v8
......@@ -8,6 +8,7 @@
#include "src/base/compiler-specific.h"
#include "src/compiler/graph-reducer.h"
#include "src/globals.h"
#include "src/machine-type.h"
namespace v8 {
namespace internal {
......@@ -17,13 +18,23 @@ namespace compiler {
class CommonOperatorBuilder;
// Propagates {Dead} control and {DeadValue} values through the graph and
// thereby removes dead code. When {DeadValue} hits the effect chain, a crashing
// {Unreachable} node is inserted and the rest of the effect chain is collapsed.
// We wait for the {EffectControlLinearizer} to connect {Unreachable} nodes to
// the graph end, since this is much easier if there is no floating control.
// We detect dead values based on types, pruning uses of DeadValue except for
// uses by phi. These remaining uses are eliminated in the
// {EffectControlLinearizer}, where they are replaced with dummy values.
// thereby removes dead code.
// We detect dead values based on types, replacing uses of nodes with
// {Type::None()} with {DeadValue}. A pure node (other than a phi) using
// {DeadValue} is replaced by {DeadValue}. When {DeadValue} hits the effect
// chain, a crashing {Unreachable} node is inserted and the rest of the effect
// chain is collapsed. We wait for the {EffectControlLinearizer} to connect
// {Unreachable} nodes to the graph end, since this is much easier if there is
// no floating control.
// {DeadValue} has an input, which has to have {Type::None()}. This input is
// important to maintain the dependency on the cause of the unreachable code.
// {Unreachable} has a value output and {Type::None()} so it can be used by
// {DeadValue}.
// {DeadValue} nodes track a {MachineRepresentation} so they can be lowered to a
// value-producing node. {DeadValue} has the runtime semantics of crashing and
// behaves like a constant of its representation so it can be used in gap moves.
// Since phi nodes are the only remaining use of {DeadValue}, this
// representation is only adjusted for uses by phi nodes.
// In contrast to {DeadValue}, {Dead} can never remain in the graph.
class V8_EXPORT_PRIVATE DeadCodeElimination final
: public NON_EXPORTED_BASE(AdvancedReducer) {
......@@ -53,15 +64,16 @@ class V8_EXPORT_PRIVATE DeadCodeElimination final
void TrimMergeOrPhi(Node* node, int size);
Node* DeadValue(Node* none_node,
MachineRepresentation rep = MachineRepresentation::kNone);
Graph* graph() const { return graph_; }
CommonOperatorBuilder* common() const { return common_; }
Node* dead() const { return dead_; }
Node* dead_value() const { return dead_value_; }
Graph* const graph_;
CommonOperatorBuilder* const common_;
Node* const dead_;
Node* const dead_value_;
Zone* zone_;
DISALLOW_COPY_AND_ASSIGN(DeadCodeElimination);
......
......@@ -318,28 +318,6 @@ void TryCloneBranch(Node* node, BasicBlock* block, Zone* temp_zone,
merge->Kill();
}
Node* DummyValue(JSGraph* jsgraph, MachineRepresentation rep) {
switch (rep) {
case MachineRepresentation::kTagged:
case MachineRepresentation::kTaggedSigned:
return jsgraph->SmiConstant(0xDEAD);
case MachineRepresentation::kTaggedPointer:
return jsgraph->TheHoleConstant();
case MachineRepresentation::kWord64:
return jsgraph->Int64Constant(0xDEAD);
case MachineRepresentation::kWord32:
return jsgraph->Int32Constant(0xDEAD);
case MachineRepresentation::kFloat64:
return jsgraph->Float64Constant(0xDEAD);
case MachineRepresentation::kFloat32:
return jsgraph->Float32Constant(0xDEAD);
case MachineRepresentation::kBit:
return jsgraph->Int32Constant(0);
default:
UNREACHABLE();
}
}
} // namespace
void EffectControlLinearizer::Run() {
......@@ -369,7 +347,6 @@ void EffectControlLinearizer::Run() {
// Iterate over the phis and update the effect phis.
Node* effect_phi = nullptr;
Node* terminate = nullptr;
int predecessor_count = static_cast<int>(block->PredecessorCount());
for (; instr < block->NodeCount(); instr++) {
Node* node = block->NodeAt(instr);
// Only go through the phis and effect phis.
......@@ -380,19 +357,7 @@ void EffectControlLinearizer::Run() {
DCHECK_NE(IrOpcode::kIfException, control->opcode());
effect_phi = node;
} else if (node->opcode() == IrOpcode::kPhi) {
DCHECK_EQ(predecessor_count, node->op()->ValueInputCount());
for (int i = 0; i < predecessor_count; ++i) {
if (NodeProperties::GetValueInput(node, i)->opcode() ==
IrOpcode::kDeadValue) {
// Phi uses of {DeadValue} must originate from unreachable code. Due
// to schedule freedom between the effect and the control chain,
// they might still appear in reachable code. So we replace them
// with a dummy value.
NodeProperties::ReplaceValueInput(
node, DummyValue(jsgraph(), PhiRepresentationOf(node->op())),
i);
}
}
// Just skip phis.
} else if (node->opcode() == IrOpcode::kTerminate) {
DCHECK_NULL(terminate);
terminate = node;
......@@ -856,6 +821,8 @@ bool EffectControlLinearizer::TryWireInStateEffect(Node* node,
case IrOpcode::kSameValue:
result = LowerSameValue(node);
break;
case IrOpcode::kDeadValue:
result = LowerDeadValue(node);
case IrOpcode::kStringFromCharCode:
result = LowerStringFromCharCode(node);
break;
......@@ -2666,6 +2633,15 @@ Node* EffectControlLinearizer::LowerSameValue(Node* node) {
__ NoContextConstant());
}
Node* EffectControlLinearizer::LowerDeadValue(Node* node) {
Node* input = NodeProperties::GetValueInput(node, 0);
if (input->opcode() != IrOpcode::kUnreachable) {
Node* unreachable = __ Unreachable();
NodeProperties::ReplaceValueInput(node, unreachable, 0);
}
return node;
}
Node* EffectControlLinearizer::LowerStringToNumber(Node* node) {
Node* string = node->InputAt(0);
......
......@@ -113,6 +113,7 @@ class V8_EXPORT_PRIVATE EffectControlLinearizer {
Node* LowerNewConsString(Node* node);
Node* LowerArrayBufferWasNeutered(Node* node);
Node* LowerSameValue(Node* node);
Node* LowerDeadValue(Node* node);
Node* LowerStringToNumber(Node* node);
Node* LowerStringCharAt(Node* node);
Node* LowerStringCharCodeAt(Node* node);
......
......@@ -134,6 +134,11 @@ Node* GraphAssembler::DebugBreak() {
current_effect_, current_control_);
}
Node* GraphAssembler::Unreachable() {
return current_effect_ = graph()->NewNode(common()->Unreachable(),
current_effect_, current_control_);
}
Node* GraphAssembler::Store(StoreRepresentation rep, Node* object, Node* offset,
Node* value) {
return current_effect_ =
......
......@@ -194,6 +194,8 @@ class GraphAssembler {
// Debugging
Node* DebugBreak();
Node* Unreachable();
Node* Float64RoundDown(Node* value);
Node* ToNumber(Node* value);
......
......@@ -251,6 +251,23 @@ class OperandGenerator {
return Constant(OpParameter<ExternalReference>(node));
case IrOpcode::kHeapConstant:
return Constant(OpParameter<Handle<HeapObject>>(node));
case IrOpcode::kDeadValue: {
switch (DeadValueRepresentationOf(node->op())) {
case MachineRepresentation::kBit:
case MachineRepresentation::kWord32:
case MachineRepresentation::kTagged:
case MachineRepresentation::kTaggedSigned:
case MachineRepresentation::kTaggedPointer:
return Constant(static_cast<int32_t>(0));
case MachineRepresentation::kFloat64:
return Constant(static_cast<double>(0));
case MachineRepresentation::kFloat32:
return Constant(static_cast<float>(0));
default:
UNREACHABLE();
}
break;
}
default:
break;
}
......
......@@ -1147,6 +1147,9 @@ void InstructionSelector::VisitNode(Node* node) {
case IrOpcode::kUnreachable:
VisitUnreachable(node);
return;
case IrOpcode::kDeadValue:
VisitDeadValue(node);
return;
case IrOpcode::kComment:
VisitComment(node);
return;
......@@ -2620,6 +2623,12 @@ void InstructionSelector::VisitUnreachable(Node* node) {
Emit(kArchDebugBreak, g.NoOutput());
}
void InstructionSelector::VisitDeadValue(Node* node) {
OperandGenerator g(this);
MarkAsRepresentation(DeadValueRepresentationOf(node->op()), node);
Emit(kArchDebugBreak, g.DefineAsConstant(node));
}
void InstructionSelector::VisitComment(Node* node) {
OperandGenerator g(this);
InstructionOperand operand(g.UseImmediate(node));
......
......@@ -352,6 +352,7 @@ class V8_EXPORT_PRIVATE InstructionSelector final {
void VisitThrow(Node* node);
void VisitRetain(Node* node);
void VisitUnreachable(Node* node);
void VisitDeadValue(Node* node);
void EmitPrepareArguments(ZoneVector<compiler::PushParameter>* arguments,
const CallDescriptor* descriptor, Node* node);
......
......@@ -305,10 +305,6 @@ Node* JSGraph::Dead() {
return CACHED(kDead, graph()->NewNode(common()->Dead()));
}
Node* JSGraph::DeadValue() {
return CACHED(kDeadValue, graph()->NewNode(common()->DeadValue()));
}
void JSGraph::GetCachedNodes(NodeVector* nodes) {
cache_.GetCachedNodes(nodes);
for (size_t i = 0; i < arraysize(cached_nodes_); i++) {
......
......@@ -155,9 +155,6 @@ class V8_EXPORT_PRIVATE JSGraph : public NON_EXPORTED_BASE(ZoneObject) {
// Create a control node that serves as dependency for dead nodes.
Node* Dead();
// Sentinel for a value resulting from unreachable computations.
Node* DeadValue();
CommonOperatorBuilder* common() const { return common_; }
JSOperatorBuilder* javascript() const { return javascript_; }
SimplifiedOperatorBuilder* simplified() const { return simplified_; }
......@@ -199,7 +196,6 @@ class V8_EXPORT_PRIVATE JSGraph : public NON_EXPORTED_BASE(ZoneObject) {
kEmptyStateValues,
kSingleDeadTypedStateValues,
kDead,
kDeadValue,
kNumCachedNodes // Must remain last.
};
......
......@@ -216,7 +216,9 @@ Node* RepresentationChanger::GetTaggedSignedRepresentationFor(
const Operator* op;
if (output_type->Is(Type::None())) {
// This is an impossible value; it should not be used at runtime.
return jsgraph()->DeadValue();
return jsgraph()->graph()->NewNode(
jsgraph()->common()->DeadValue(MachineRepresentation::kTaggedSigned),
node);
} else if (IsWord(output_rep)) {
if (output_type->Is(Type::Signed31())) {
op = simplified()->ChangeInt31ToTaggedSigned();
......@@ -336,7 +338,9 @@ Node* RepresentationChanger::GetTaggedPointerRepresentationFor(
Operator const* op;
if (output_type->Is(Type::None())) {
// This is an impossible value; it should not be used at runtime.
return jsgraph()->DeadValue();
return jsgraph()->graph()->NewNode(
jsgraph()->common()->DeadValue(MachineRepresentation::kTaggedPointer),
node);
} else if (output_rep == MachineRepresentation::kBit) {
if (output_type->Is(Type::Boolean())) {
op = simplified()->ChangeBitToTagged();
......@@ -413,7 +417,8 @@ Node* RepresentationChanger::GetTaggedRepresentationFor(
const Operator* op;
if (output_type->Is(Type::None())) {
// This is an impossible value; it should not be used at runtime.
return jsgraph()->DeadValue();
return jsgraph()->graph()->NewNode(
jsgraph()->common()->DeadValue(MachineRepresentation::kTagged), node);
} else if (output_rep == MachineRepresentation::kBit) {
if (output_type->Is(Type::Boolean())) {
op = simplified()->ChangeBitToTagged();
......@@ -491,7 +496,8 @@ Node* RepresentationChanger::GetFloat32RepresentationFor(
const Operator* op = nullptr;
if (output_type->Is(Type::None())) {
// This is an impossible value; it should not be used at runtime.
return jsgraph()->DeadValue();
return jsgraph()->graph()->NewNode(
jsgraph()->common()->DeadValue(MachineRepresentation::kFloat32), node);
} else if (IsWord(output_rep)) {
if (output_type->Is(Type::Signed32())) {
// int32 -> float64 -> float32
......@@ -551,7 +557,8 @@ Node* RepresentationChanger::GetFloat64RepresentationFor(
const Operator* op = nullptr;
if (output_type->Is(Type::None())) {
// This is an impossible value; it should not be used at runtime.
return jsgraph()->DeadValue();
return jsgraph()->graph()->NewNode(
jsgraph()->common()->DeadValue(MachineRepresentation::kFloat64), node);
} else if (IsWord(output_rep)) {
if (output_type->Is(Type::Signed32())) {
op = machine()->ChangeInt32ToFloat64();
......@@ -628,7 +635,8 @@ Node* RepresentationChanger::GetWord32RepresentationFor(
const Operator* op = nullptr;
if (output_type->Is(Type::None())) {
// This is an impossible value; it should not be used at runtime.
return jsgraph()->DeadValue();
return jsgraph()->graph()->NewNode(
jsgraph()->common()->DeadValue(MachineRepresentation::kWord32), node);
} else if (output_rep == MachineRepresentation::kBit) {
return node; // Sloppy comparison -> word32
} else if (output_rep == MachineRepresentation::kFloat64) {
......@@ -767,7 +775,8 @@ Node* RepresentationChanger::GetBitRepresentationFor(
const Operator* op;
if (output_type->Is(Type::None())) {
// This is an impossible value; it should not be used at runtime.
return jsgraph()->DeadValue();
return jsgraph()->graph()->NewNode(
jsgraph()->common()->DeadValue(MachineRepresentation::kBit), node);
} else if (output_rep == MachineRepresentation::kTagged ||
output_rep == MachineRepresentation::kTaggedPointer) {
if (output_type->Is(Type::BooleanOrNullOrUndefined())) {
......@@ -812,7 +821,8 @@ Node* RepresentationChanger::GetWord64RepresentationFor(
Node* node, MachineRepresentation output_rep, Type* output_type) {
if (output_type->Is(Type::None())) {
// This is an impossible value; it should not be used at runtime.
return jsgraph()->DeadValue();
return jsgraph()->graph()->NewNode(
jsgraph()->common()->DeadValue(MachineRepresentation::kWord32), node);
} else if (output_rep == MachineRepresentation::kBit) {
return node; // Sloppy comparison -> word64
}
......
......@@ -965,7 +965,7 @@ Type* Typer::Visitor::TypeDead(Node* node) { return Type::None(); }
Type* Typer::Visitor::TypeDeadValue(Node* node) { return Type::None(); }
Type* Typer::Visitor::TypeUnreachable(Node* node) { UNREACHABLE(); }
Type* Typer::Visitor::TypeUnreachable(Node* node) { return Type::None(); }
// JS comparison operators.
......
......@@ -236,10 +236,19 @@ void Verifier::Visitor::Check(Node* node, const AllNodes& all) {
// Dead is never connected to the graph.
UNREACHABLE();
case IrOpcode::kDeadValue:
CheckValueInputIs(node, 0, Type::None());
CheckTypeIs(node, Type::None());
break;
case IrOpcode::kUnreachable:
CheckNotTyped(node);
CheckTypeIs(node, Type::None());
for (Edge edge : node->use_edges()) {
Node* use = edge.from();
if (NodeProperties::IsValueEdge(edge) && all.IsLive(use)) {
// {Unreachable} nodes can only be used by {DeadValue}, because they
// don't actually produce a value.
CHECK_EQ(IrOpcode::kDeadValue, use->opcode());
}
}
break;
case IrOpcode::kBranch: {
// Branch uses are IfTrue and IfFalse.
......
// Copyright 2018 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flags: --allow-natives-syntax
'use strict';
function f(abort, n, a, b) {
if (abort) return;
var x = a ? true : "" + a;
if (!a) {
var dead = n + 1 + 1;
if(!b) {
x = dead;
}
if (x) {
x = false;
}
if (b) {
x = false;
}
}
return x + 1;
}
f(false, 5); f(false, 6); f(false, 7); f(false, 8);
function g(abort, a, b) {
return f(abort, "abc", a, b);
}
g(true); g(true); g(true); g(true);
%OptimizeFunctionOnNextCall(g);
g(false);
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment