m-labs / artiq

A leading-edge control system for quantum information experiments
https://m-labs.hk/artiq
GNU Lesser General Public License v3.0
434 stars 200 forks source link

Core device number casting #1242

Closed drewrisinger closed 5 years ago

drewrisinger commented 5 years ago

Bug Report

One-Line Summary

Inconsistent results when casting binary numbers.

Issue Details

Steps to Reproduce

Sample Program:

import numpy as np

from artiq.language import EnvExperiment, kernel, delay, us

def prnt(fmt, *a):
    # sign-extend then chop
    sign_corrected_args = tuple((np.int64(arg) & 0xFFFFFFFF for arg in a))
    print(fmt % sign_corrected_args)

class TestCast1242(EnvExperiment):
    """Experiment to test issue m-labs#1242."""

    def build(self):
         self.core = self.get_device("core")

    @kernel
    def run(self):
        self.core.reset()
        self.core.break_realtime()
        delay(1000 * us)

        # I know it's not best practice but for running this to make sure that vars were completely isolated,
        # I just commented/uncommented following lines to run with ``artiq_run``
        val = 0x199999999999
        # val = np.int64(0x199999999999)

        # Upper bit statements
        val_upper_32 = (val >> 32) & 0xFFFFFFFF  # Up #1
        # val_upper_32 = np.int32(val / 65536 / 65536) # Up #2
        # val_upper_32 = np.int32(val >> 32)  # Up #3
        # val_upper_32 = np.int32(np.int64(val >>32)) # Up #4

        # Lower 32 bits
        # val_lower_32 = val & 0xFFFFFFFF # Works with all upper statements
        # val_lower_32 = np.int32(np.int64(val))  # Works with all upper statements
        val_lower_32 = np.int32(val) # Doesn't work with Up #1 (0xFFFF FFFF), not with Up #2 (0x0000 0000), not with Up #3 (0xFFFF FFFF), not with Up #4 (0x0000 0000)

        prnt("Upper: 0x%08X, Lower: 0x%08X", val_upper_32, val_lower_32)

Expected Behavior

All combinations of statements should output same values: (0x0000 1999, 0x9999 9999)

Actual (undesired) Behavior

Any of the upper bit statements combined with the last lower statement (val_lower_32 = np.int32(val)) fail when val = 0x1999 9999 9999, but work when val = np.int64(0x1999 9999 9999). See comment below for failure table.

My best guess is this is either a compiler error (maybe the type inferencer), or a reorder buffer & ALU overflow bit issue (it shouldn't be possible for a later type cast to affect the value of the upper bits, but it apparently does).

Understanding that this will probably take a while to get fixed, is the consensus that we should just explicitly declare the type of all integers to avoid similar issues?

Your System

drewrisinger commented 5 years ago
val=0x1999 9999 9999      
  lower= val & 0xFFFF FFFF lower= np.int32(np.int64(val)) lower=np.int32(val)
upper= (val >> 32) & 0xFFFF FFFF 0x0000 1999, 0x9999 9999 0x0000 1999, 0x9999 9999 0xFFFF FFFF, 0x9999 9999
upper= np.int32(val / 65536 / 65536) 0x0000 1999, 0x9999 9999 0x0000 1999, 0x9999 9999 0x0000 0000, 0x9999 9999
upper= np.int32(val >> 32) 0x0000 1999, 0x9999 9999 0x0000 1999, 0x9999 9999 0xFFFF FFFF, 0x9999 9999
upper= np.int32(np.int64(val / 65536 / 65536)) 0x0000 1999, 0x9999 9999 0x0000 1999, 0x9999 9999 0x0000 0000, 0x9999 9999

Did not completely test with setting val=np.int64(0x1999 9999 9999), but all problems above did not appear.

drewrisinger commented 5 years ago

Semi-related: does ARTIQ's LLVM not do a constant folding pass? Cause if so, this shouldn't be an issue unless it's an error in the compiler's type handling.

whitequark commented 5 years ago

This is expected (although undesirable) behavior that results from the way overflowing operations interact with polymorphic integer literals and type inference. It was well known to me that this kind of issue would arise when I was implementing it and hopefully I've commucated that to @sbourdeauducq.

I invite anyone with good ideas on solving this problem within the constraints (i.e. type inference, polymorphic literals and overflowing integers) to suggest their solution here. One unfortunate consequence of this design already being used is that any new design must not silently change the meaning of existing, even buggy, code. That makes it much harder.

sbourdeauducq commented 5 years ago

No. How is val anything other than a 64-bit integer in the first place?