Giter VIP home page Giter VIP logo

Comments (3)

HertzDevil avatar HertzDevil commented on June 5, 2024 2

Reduced:

struct Time2
  def initialize(@seconds : Int64)
    @nanoseconds = uninitialized UInt32[3]
  end

  def <(other : Time2) : Bool
    @seconds < other.@seconds
  end
end

class Constraints::Range
  def initialize(@min : Int128 | Time2 | Nil)
  end
end

def validate(value : Time2, constraint) : Nil
  min = constraint.@min

  if min.is_a?(Time2?)
    if min
      if value < min
        LibC.printf("foo\n")
      end
    end
  end
end

value = Time2.new(123)
constraint = Constraints::Range.new(Time2.new(45))
validate(value, constraint)

This generates the following fragment for the #< call:

then2:                                            ; preds = %then
  %25 = getelementptr inbounds %"(Int128 | Time2 | Nil)", ptr %min, i32 0, i32 0
  %26 = load i32, ptr %25, align 4
  %27 = getelementptr inbounds %"(Int128 | Time2 | Nil)", ptr %min, i32 0, i32 1
  %28 = load %Time2, ptr %27, align 8 ; underaligned load occurs here
  %29 = call i1 @"*Time2#<<Time2>:Bool"(ptr %value1, %Time2 %28)
  br i1 %29, label %then4, label %else5

It looks like all loads that go through union_type_and_value_pointer must be aligned.

from crystal.

HertzDevil avatar HertzDevil commented on June 5, 2024 2

Changing the alignments in the emitted LLVM IR manually did not help. Instead it looks like the problem is with how the new memcpy in #14279 always picks the size of the larger union, regardless of whether an upcast or downcast is performed:

if min.is_a?(Time2?)
  if min # this downcast
    # ...
%"(Time2 | Nil)" = type { i32, [3 x i64] }

%11 = getelementptr inbounds %"(Int128 | Time2 | Nil)", ptr %min, i32 0, i32 0
%12 = load i32, ptr %11, align 4
%14 = getelementptr inbounds %"(Time2 | Nil)", ptr %0, i32 0, i32 0
store i32 %12, ptr %14, align 4
%15 = getelementptr inbounds %"(Time2 | Nil)", ptr %0, i32 0, i32 1
%16 = getelementptr inbounds %"(Int128 | Time2 | Nil)", ptr %min, i32 0, i32 1
call void @llvm.memcpy.p0.p0.i64(ptr align 8 %15, ptr align 16 %16, i64 32, i1 false) ; this 32

So we are corrupting the stack by copying 32 bytes to the data field of a Time2 | Nil, which only measures 24 bytes. Using the less size among the two unions seems to fix the issue.

We probably don't need to touch the load / store alignments for now. Maybe LLVM cannot prove that some align 16 values are always satisfied...?

from crystal.

HertzDevil avatar HertzDevil commented on June 5, 2024

The problem is that the getelementptr and the load instructions are not necessarily emitted in the same place. For example, the following part of the compiler handles the snippet above:

request_value(arg)
if arg.type.void?
call_arg = int8(0)
else
call_arg = @last
call_arg = llvm_nil if arg.type.nil_type?
call_arg = downcast(call_arg, def_arg.type, arg.type, true)
end
# - C calling convention passing needs a separate handling of pass-by-value
# - Primitives might need a separate handling (for example invoking a Proc)
if arg.type.passed_by_value? && !c_calling_convention && !is_primitive
call_arg = load(llvm_type(arg.type), call_arg)
end

%27 is emitted by the request_value call on line 85 (which calls CodeGenVisitor#visit(Var) etc.), but %28 is emitted by the load on line 98, so we have to somehow carry this alignment information across those lines, otherwise %28 will pick up the alignment from alignof(Time2) automatically. Yet only the relevant LLVM instructions have alignment, not LLVM types, for reasons similar to the recent transition to opaque pointers.

It was mostly coincidence that underaligned operations for smaller sizes were working before on x86-64. With actual 16-byte-aligned types this is not the case anymore.

from crystal.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.