Comments (3)
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.
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.
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:
crystal/src/compiler/crystal/codegen/call.cr
Lines 85 to 99 in c67883c
%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)
- Range#size returns an Union instead of an Int32 HOT 1
- Inconsistent behaviour regarding line break requirement in method definition HOT 1
- `Module validation failed` with proc in recursive def HOT 3
- Crystal REPL instantly executes statement with intertwined heredoc HOT 1
- FormData part.body.read does not fill buffer HOT 3
- `Hash#rehash` removes `@first` elements from hash
- Add API for `Base64.encode` / `Base64.decode` with an `IO` as the source HOT 3
- Add methods for filling a buffer from an IO greedily/lazily HOT 5
- Enable `ameba` in this repo HOT 19
- formatter cause syntax error. HOT 2
- `Crypto::Subtle.constant_time_compare` does not work with `StaticArray` / Cannot overwrite `StaticArray` variable HOT 2
- Parser failure on argument list with trailing comma HOT 6
- RFC: Tracing HOT 8
- `Crystal::System::User#from_*?` et al. don't work if required buffer size greater than initial buffer size HOT 1
- If any system user entry exceeds `GETPW_R_SIZE_MAX`, retreiving any user is impossible HOT 2
- `IO#same_content?` returns `true` if `stream1` is a prefix of `stream2` HOT 1
- `docs_main.cr` is a mess HOT 1
- Interpreter Error: can't cast Pointer(Void) to (Pointer(Void) | String)
- Parser considers empty argument list with trailing comma as invalid HOT 5
- Sockets are inherited by subprocesses HOT 5
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
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.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from crystal.