def _insert_op_before(self, new_op: Operation, before_op: Optional[Operation] = None):
        """
        A private API used by builder. Please use `builder.YOUR_OP(...,before_op)`.

        new_op's outputs are not used (not input to any other op) after
        this call. All inputs to new_op must be visible at or before
        the before_op (i.e., new_op must be added in topologically sorted
        order). Note that this is more restrictive than MIL, whose Block
        supports lexical scoping and thus an op can reference Var in enclosing
        scopes. new_op.name must be unique in the block.

        before_op=None to append new_op at the end of self.operations.

        Given:   %2 = op0(%1, %1)
                 %4 = op2(%1)
                 %6 = op3(%4, %4)

        Execute: insert_op_before(op1, before_op=op2),
                 where %3 = op1(%1, %2)

        Result:  %2 = op0(%1, %1)
                 %3 = op1(%1, %2)
                 %4 = op2(%1)
                 %6 = op3(%4, %4)

        Comment: We assume op1 has been constructed outside the block with
        %1, %2 as inputs. Typically it's builder's job to create an op and
        insert into the current block.

        Comment: insert_op_before(op1, before_op=op0) would error as %2 (an input to op1)
        is not visible before op0.
        """
        self.validate()

        if isinstance(self.operations, CacheDoublyLinkedList):
            self.operations.insert_op_before(new_op, before_op)
            return

        if before_op is None:
            self.operations.append(new_op)
            return

        # check inputs visibility in debug mode
        if DEBUG:
            for k, v in new_op.inputs.items():
                if not isinstance(v, (Var, tuple)):
                    continue
                vs = [v] if isinstance(v, Var) else v
                for v in vs:
                    if not self.is_var_visible_in_block(v, upto_op=before_op):
                        before_op_name = before_op.name if before_op is not None else "None"
                        msg = "Op '{}' input {}={} is not in scope of {} before {}"
                        raise ValueError(
                            msg.format(new_op.name, k, v.name, self.name, before_op_name)
                        )

        idx = self.find_op_id_in_block(before_op)
        self.operations.insert(idx, new_op)

        A private API used by builder. Please use `builder.YOUR_OP(...,before_op)`.

        new_op's outputs are not used (not input to any other op) after
        this call. All inputs to new_op must be visible at or before
        the before_op (i.e., new_op must be added in topologically sorted
        order). Note that this is more restrictive than MIL, whose Block
        supports lexical scoping and thus an op can reference Var in enclosing
        scopes. new_op.name must be unique in the block.

        before_op=None to append new_op at the end of self.operations.

        Given:   %2 = op0(%1, %1)
                 %4 = op2(%1)
                 %6 = op3(%4, %4)

        Execute: insert_op_before(op1, before_op=op2),
                 where %3 = op1(%1, %2)

        Result:  %2 = op0(%1, %1)
                 %3 = op1(%1, %2)
                 %4 = op2(%1)
                 %6 = op3(%4, %4)

        Comment: We assume op1 has been constructed outside the block with
        %1, %2 as inputs. Typically it's builder's job to create an op and
        insert into the current block.

        Comment: insert_op_before(op1, before_op=op0) would error as %2 (an input to op1)
        is not visible before op0.
        
coremltools/converters/mil/mil/block.py