仅 64 位释放模式下的移位非法指令

bit-shift illegal instruction in 64-bit release mode only

我正在将可用的 PoissonRecon 代码 here 集成到我自己的网格操作代码中,但我无法在 x64 中编译集成代码,因为每一位都存在 "illegal instruction" 错误-泊松码八叉树生成中的移位指令。

我使用的是 Visual Studio 2015,我的问题仅在 x64 编译且仅在发布模式下出现(即,它适用于 x86 调试和发布以及 x64 调试)。

作为移位指令之一的示例,在Octree.inl的顶部定义了以下内容:

template< class NodeData > const int OctNode< NodeData >::DepthShift=5;
template< class NodeData > const int OctNode< NodeData >::OffsetShift = ( sizeof(long long)*8 - DepthShift ) / 3;
template< class NodeData > const int OctNode< NodeData >::DepthMask=(1<<DepthShift)-1; // This variable is correct
template< class NodeData > const int OctNode< NodeData >::OffsetMask=(1<<OffsetShift)-1; // This variable is also correct
template< class NodeData > const int OctNode< NodeData >::OffsetShift1=DepthShift;
template< class NodeData > const int OctNode< NodeData >::OffsetShift2=OffsetShift1+OffsetShift;
template< class NodeData > const int OctNode< NodeData >::OffsetShift3=OffsetShift2+OffsetShift;

并且这些变量在以下函数中使用:

template< class NodeData >
inline unsigned long long OctNode< NodeData >::Index( int depth , const int offset[3] )
{
    unsigned long long idx=0;
    idx |= ( ( (unsigned long long)(depth    ) ) & DepthMask  );
    idx |= ( ( (unsigned long long)(offset[0]) ) & OffsetMask ) << OffsetShift1;
    idx |= ( ( (unsigned long long)(offset[1]) ) & OffsetMask ) << OffsetShift2;
    idx |= ( ( (unsigned long long)(offset[2]) ) & OffsetMask ) << OffsetShift3;
    return idx;
}

这个函数在第

行中断
idx |= ( ( (unsigned long long)(offset[0]) ) & OffsetMask ) << OffsetShift1;

我进一步分解后发现问题出在位移本身,即 (var)<<OffsetShift1; 但这会导致 "illegal instruction" 错误。

请注意,OffsetShift1 只是“5”,因此这相当于 (var)<<5;,它确实按预期工作。

一个可能的解决方法是简单地 #define 顶部的所有这些变量(这确实解决了问题),但这并没有解决其他移位问题,例如:

void _startAndWidth( const TreeOctNode* node , Point3D< Real >& start , Real& width ) const
    {
        LocalDepth d ; LocalOffset off;
        _localDepthAndOffset( node , d , off );
        if (d >= 0) width = Real(1.0 / (1 << d));
        else width = Real( 1.0 * (1<<(-d)) );
        for( int dd=0 ; dd<DIMENSION ; dd++ ) start[dd] = Real( off[dd] ) * width;
    }

我已经尝试 static_cast<long long> 一切,但这不是溢出问题。更奇怪的是,如果我在位移之前中断(在释放模式下)然后突出显示操作,调试器会告诉我正确的结果(例如,_startAndWidth 中的 d = 5,所以 1 << d returns 32 在调试器中)但实际上单步执行操作会导致 "illegal instruction" 错误。

由于程序在调试模式下运行正常,我尝试在发布模式下删除所有优化,但我仍然遇到同样的错误。我发现的唯一解决方法是用 pow() 替换所有移位操作,虽然可行,但似乎 bit 荒谬。

事实证明,重建代码附带的.sln文件是用AVX2设置的,我的机器不支持。

转到 Configuration Properties >> C/C++ >> Code Generation 并将 "Enable Enhanced Instruction Set" 设置为 AVX 解决了这个问题。

汇编代码现在显示 "SHL" 而不是 "SHLX," 所以 "illegal instruction" 是实际的 SHLX 命令不可用,不是进入 SHLX 的参数的问题。