<commit_before>// Copyright (c) 2012, the Dart project authors.  Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.

import '../compiler.dart' show Compiler;
import '../constants/values.dart';
import '../elements/elements.dart';
import '../js_backend/js_backend.dart';
import '../types/types.dart';
import '../universe/selector.dart' show Selector;
import 'nodes.dart';

/**
 * Replaces some instructions with specialized versions to make codegen easier.
 * Caches codegen information on nodes.
 */
class SsaInstructionSelection extends HBaseVisitor {
  final Compiler compiler;
  HGraph graph;

  SsaInstructionSelection(this.compiler);

  JavaScriptBackend get backend => compiler.backend;

  void visitGraph(HGraph graph) {
    this.graph = graph;
    visitDominatorTree(graph);
  }

  visitBasicBlock(HBasicBlock block) {
    HInstruction instruction = block.first;
    while (instruction != null) {
      HInstruction next = instruction.next;
      HInstruction replacement = instruction.accept(this);
      if (replacement != instruction && replacement != null) {
        block.rewrite(instruction, replacement);

        // If the replacement instruction does not know its source element, use
        // the source element of the instruction.
        if (replacement.sourceElement == null) {
          replacement.sourceElement = instruction.sourceElement;
        }
        if (replacement.sourceInformation == null) {
          replacement.sourceInformation = instruction.sourceInformation;
        }
        if (!replacement.isInBasicBlock()) {
          // The constant folding can return an instruction that is already
          // part of the graph (like an input), so we only add the replacement
          // if necessary.
          block.addAfter(instruction, replacement);
          // Visit the replacement as the next instruction in case it can also
          // be constant folded away.
          next = replacement;
        }
        block.remove(instruction);
      }
      instruction = next;
    }
  }

  HInstruction visitInstruction(HInstruction node) {
    return node;
  }

  HInstruction visitIs(HIs node) {
    if (node.kind == HIs.RAW_CHECK) {
      HInstruction interceptor = node.interceptor;
      if (interceptor != null) {
        return new HIsViaInterceptor(
            node.typeExpression, interceptor, backend.boolType);
      }
    }
    return node;
  }

  HInstruction visitIdentity(HIdentity node) {
    node.singleComparisonOp = simpleOp(node.left, node.right);
    return node;
  }

  String simpleOp(HInstruction left, HInstruction right) {
    // Returns the single identity comparison (== or ===) or null if a more
    // complex expression is required.
    TypeMask leftType = left.instructionType;
    TypeMask rightType = right.instructionType;
    if (leftType.isNullable && rightType.isNullable) {
      if (left.isConstantNull() ||
          right.isConstantNull() ||
          (left.isPrimitive(compiler) && leftType == rightType)) {
        return '==';
      }
      return null;
    }
    return '===';
  }

  HInstruction visitInvokeDynamic(HInvokeDynamic node) {
    if (node.isInterceptedCall) {
      // Calls of the form
      //
      //     a.foo$1(a, x)
      //
      // where the interceptor calling convention is used come from recognizing
      // that 'a' is a 'self-interceptor'.  If the selector matches only methods
      // that ignore the explicit receiver parameter, replace occurences of the
      // receiver argument with a dummy receiver '0':
      //
      //     a.foo$1(a, x)   --->   a.foo$1(0, x)
      //
      // This often reduces the number of references to 'a' to one, allowing 'a'
      // to be generated at use to avoid a temporary, e.g.
      //
      //     t1 = b.get$thing();
      //     t1.foo$1(t1, x)
      // --->
      //     b.get$thing().foo$1(0, x)
      //
      Selector selector = node.selector;
      TypeMask mask = node.mask;
      if (backend.isInterceptedSelector(selector) &&
          !backend.isInterceptedMixinSelector(selector, mask)) {
        HInstruction interceptor = node.inputs[0];
        HInstruction receiverArgument = node.inputs[1];

        if (interceptor.nonCheck() == receiverArgument.nonCheck()) {
          // TODO(15933): Make automatically generated property extraction
          // closures work with the dummy receiver optimization.
          if (!selector.isGetter) {
            ConstantValue constant = new SyntheticConstantValue(
                SyntheticConstantKind.DUMMY_INTERCEPTOR,
                receiverArgument.instructionType);
            HConstant dummy = graph.addConstant(constant, compiler);
            receiverArgument.usedBy.remove(node);
            node.inputs[1] = dummy;
            dummy.usedBy.add(node);
          }
        }
      }
    }

    return node;
  }

  HInstruction visitFieldSet(HFieldSet setter) {
    // Pattern match
    //     t1 = x.f; t2 = t1 + 1; x.f = t2; use(t2)   -->  ++x.f
    //     t1 = x.f; t2 = t1 op y; x.f = t2; use(t2)  -->  x.f op= y
    //     t1 = x.f; t2 = t1 + 1; x.f = t2; use(t1)   -->  x.f++
    HBasicBlock block = setter.block;
    HInstruction op = setter.value;
    HInstruction receiver = setter.receiver;

    bool isMatchingRead(HInstruction candidate) {
      if (candidate is HFieldGet) {
        if (candidate.element != setter.element) return false;
        if (candidate.receiver != setter.receiver) return false;
        // Recognize only three instructions in sequence in the same block.  This
        // could be broadened to allow non-interfering interleaved instructions.
        if (op.block != block) return false;
        if (candidate.block != block) return false;
        if (setter.previous != op) return false;
        if (op.previous != candidate) return false;
        return true;
      }
      return false;
    }

    HInstruction noMatchingRead() {
      // If we have other HFieldSet optimizations, they go here.<commit_msg>Dummy receiver optimization for super calls<commit_after>// Copyright (c) 2012, the Dart project authors.  Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.

import '../compiler.dart' show Compiler;
import '../constants/values.dart';
import '../elements/elements.dart';
import '../js_backend/js_backend.dart';
import '../types/types.dart';
import '../universe/selector.dart' show Selector;
import 'nodes.dart';

/**
 * Replaces some instructions with specialized versions to make codegen easier.
 * Caches codegen information on nodes.
 */
class SsaInstructionSelection extends HBaseVisitor {
  final Compiler compiler;
  HGraph graph;

  SsaInstructionSelection(this.compiler);

  JavaScriptBackend get backend => compiler.backend;

  void visitGraph(HGraph graph) {
    this.graph = graph;
    visitDominatorTree(graph);
  }

  visitBasicBlock(HBasicBlock block) {
    HInstruction instruction = block.first;
    while (instruction != null) {
      HInstruction next = instruction.next;
      HInstruction replacement = instruction.accept(this);
      if (replacement != instruction && replacement != null) {
        block.rewrite(instruction, replacement);

        // If the replacement instruction does not know its source element, use
        // the source element of the instruction.
        if (replacement.sourceElement == null) {
          replacement.sourceElement = instruction.sourceElement;
        }
        if (replacement.sourceInformation == null) {
          replacement.sourceInformation = instruction.sourceInformation;
        }
        if (!replacement.isInBasicBlock()) {
          // The constant folding can return an instruction that is already
          // part of the graph (like an input), so we only add the replacement
          // if necessary.
          block.addAfter(instruction, replacement);
          // Visit the replacement as the next instruction in case it can also
          // be constant folded away.
          next = replacement;
        }
        block.remove(instruction);
      }
      instruction = next;
    }
  }

  HInstruction visitInstruction(HInstruction node) {
    return node;
  }

  HInstruction visitIs(HIs node) {
    if (node.kind == HIs.RAW_CHECK) {
      HInstruction interceptor = node.interceptor;
      if (interceptor != null) {
        return new HIsViaInterceptor(
            node.typeExpression, interceptor, backend.boolType);
      }
    }
    return node;
  }

  HInstruction visitIdentity(HIdentity node) {
    node.singleComparisonOp = simpleOp(node.left, node.right);
    return node;
  }

  String simpleOp(HInstruction left, HInstruction right) {
    // Returns the single identity comparison (== or ===) or null if a more
    // complex expression is required.
    TypeMask leftType = left.instructionType;
    TypeMask rightType = right.instructionType;
    if (leftType.isNullable && rightType.isNullable) {
      if (left.isConstantNull() ||
          right.isConstantNull() ||
          (left.isPrimitive(compiler) && leftType == rightType)) {
        return '==';
      }
      return null;
    }
    return '===';
  }

  HInstruction visitInvokeDynamic(HInvokeDynamic node) {
    if (node.isInterceptedCall) {
      tryReplaceInterceptorWithDummy(node, node.selector, node.mask);
    }
    return node;
  }

  HInstruction visitInvokeSuper(HInvokeSuper node) {
    if (node.isInterceptedCall) {
      TypeMask mask = node.getDartReceiver(compiler).instructionType;
      tryReplaceInterceptorWithDummy(node, node.selector, mask);
    }
    return node;
  }

  void tryReplaceInterceptorWithDummy(
      HInvoke node, Selector selector, TypeMask mask) {
    // Calls of the form
    //
    //     a.foo$1(a, x)
    //
    // where the interceptor calling convention is used come from recognizing
    // that 'a' is a 'self-interceptor'.  If the selector matches only methods
    // that ignore the explicit receiver parameter, replace occurences of the
    // receiver argument with a dummy receiver '0':
    //
    //     a.foo$1(a, x)   --->   a.foo$1(0, x)
    //
    // This often reduces the number of references to 'a' to one, allowing 'a'
    // to be generated at use to avoid a temporary, e.g.
    //
    //     t1 = b.get$thing();
    //     t1.foo$1(t1, x)
    // --->
    //     b.get$thing().foo$1(0, x)
    //

    // TODO(15933): Make automatically generated property extraction closures
    // work with the dummy receiver optimization.
    if (selector.isGetter) return;

    // This assignment of inputs is uniform for HInvokeDynamic and HInvokeSuper.
    HInstruction interceptor = node.inputs[0];
    HInstruction receiverArgument = node.inputs[1];

    if (interceptor.nonCheck() == receiverArgument.nonCheck()) {
      if (backend.isInterceptedSelector(selector) &&
          !backend.isInterceptedMixinSelector(selector, mask)) {
        ConstantValue constant = new SyntheticConstantValue(
            SyntheticConstantKind.DUMMY_INTERCEPTOR,
            receiverArgument.instructionType);
        HConstant dummy = graph.addConstant(constant, compiler);
        receiverArgument.usedBy.remove(node);
        node.inputs[1] = dummy;
        dummy.usedBy.add(node);
      }
    }
  }

  HInstruction visitFieldSet(HFieldSet setter) {
    // Pattern match
    //     t1 = x.f; t2 = t1 + 1; x.f = t2; use(t2)   -->  ++x.f
    //     t1 = x.f; t2 = t1 op y; x.f = t2; use(t2)  -->  x.f op= y
    //     t1 = x.f; t2 = t1 + 1; x.f = t2; use(t1)   -->  x.f++
    HBasicBlock block = setter.block;
    HInstruction op = setter.value;
    HInstruction receiver = setter.receiver;

    bool isMatchingRead(HInstruction candidate) {
      if (candidate is HFieldGet) {
        if (candidate.element != setter.element) return false;
        if (candidate.receiver != setter.receiver) return false;
        // Recognize only three instructions in sequence in the same block.  This
        // could be broadened to allow non-interfering interleaved instructions.
        if (op.block != block) return false;
        if (candidate.block != block) return false;
        if (setter.previous != op) return false;
        if (op.previous != candidate) return false;
        return true;
      }
      return false;
    }

    HInstruction noMatchingRead() {
      // If we have other HFieldSet optimizations, they go here.
{'fraction_non_alphanumeric': 0.10389195236953569, 'fraction_numerical': 0.0063933509150483495, 'mean_word_length': 2.496507404302878, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 15, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5891347', 'n_tokens_mistral': 3545, 'n_tokens_neox': 3518, 'n_words': 1047}