Skip to content
This repository was archived by the owner on Apr 28, 2023. It is now read-only.

Conversation

@prigoyal
Copy link
Contributor

@prigoyal prigoyal commented Apr 25, 2018

adding % operator support

closes #290

@prigoyal prigoyal added the wip label Apr 25, 2018
@prigoyal prigoyal changed the title [wip] Add modulo operator and propagate it from parser to halide to isl [wip] Add modulo operator and propagate it from parser -> halide -> isl Apr 25, 2018
@prigoyal prigoyal changed the title [wip] Add modulo operator and propagate it from parser -> halide -> isl [wip] Add % operator and propagate it from parser -> halide -> isl Apr 25, 2018
Check(
R"(
def local_sparse_convolution(float(N, C, H, W) I, float(O, KC, KH, KW) W1) -> (O) {
O(n, o, h, w) +=! I(n, (c + kc) % c, h + kh, w + kw) * W1(o, kc, kh, kw) where c in 0:C
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't (c + kc) % c equal to (kc % c) ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah, I just borrowed the example from what user posted in #290

it did feel a bit weird :D

@abadams
Copy link
Contributor

abadams commented Apr 25, 2018

This all makes sense to me so far.

@ftynse
Copy link
Contributor

ftynse commented Apr 25, 2018

When you get to Halide -> isl conversion, there may be a need to differentiate between right hand sides of % that appear in index expressions. If those are compile-time constants, we can express them as quasi-affine expressions and have exact polyhedral analyses. Otherwise, we need to overapproximate like we do for indirections.

@prigoyal
Copy link
Contributor Author

thanks @ftynse . I looked further and tried to understand the tc2halide and halide2isl better. It looks like the segfault happens because of infinite recursion in https://github.com/facebookresearch/TensorComprehensions/blob/master/tc/core/halide2isl.cc#L203 . I fixed that bug and test seems to pass now :)

@prigoyal prigoyal changed the title [wip] Add % operator and propagate it from parser -> halide -> isl Add % operator and propagate it from parser -> halide -> isl Apr 30, 2018
@prigoyal prigoyal added review me and removed wip labels Apr 30, 2018
@prigoyal prigoyal force-pushed the add-operators branch 2 times, most recently from 5662537 to cad7cf3 Compare April 30, 2018 19:57
@prigoyal prigoyal requested a review from ftynse April 30, 2018 20:02
void visit(const Variable* op) {
if (!included.count(op->name)) {
if (op->param.defined()) {
// why is this empty? - what's the point of this?
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't quite understand why this is empty and added a comment. if one of the reviewers can explain it, that'd be great and I can remove this comment as well

// We cannot span multiple constraints if a modulo operation is involved.
// x > max(a,b) % C is not equivalent to (x > a % C && x > b % C).
auto lhs = makeIslAffBoundsFromExpr(space, e, false, false);
auto lhs = makeIslAffBoundsFromExpr(space, op->a, false, false);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

using e would lead to infinite recursion and hence segfault

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This comment should be in the commit message.

}
dims.push_back(Variable::make(Int(32), p.name(), p));
}
lang::TensorType type = p.tensorType();
Copy link
Contributor Author

@prigoyal prigoyal Apr 30, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

there is no need for else here since we return from the if condition if satisfied. cc @abadams, hope this is okay?

// funcs, bounds, reductions will be populated
void translateComprehension(
const lang::Comprehension& c,
const lang::Comprehension& comprehension,
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

while reading the code, it was confusing to me to read c so I made it more descriptive to facilitate code readability. Hope this is okay, otherwise I can revert too.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1, one character names are for iteration variables of short loops

}
return matched_type;
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was repeatedly getting confused about where a function started and ended. So I added these white lines to help. is this okay @zdevito ? :)


TreeRef floatType(TreeRef anchor) {
return c(TK_FLOAT, anchor->range(), {});
return createCompound(TK_FLOAT, anchor->range(), {});
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

simple c was confusing to me as a function name.

Copy link
Contributor

@nicolasvasilache nicolasvasilache left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see at least 5 independent pieces here:

  1. documentation improvements
  2. legibility improvements
  3. non-behavioral code changes (early exit if vs else)
  4. change makeIslAffBoundsFromExpr(space, e, false, false); -> makeIslAffBoundsFromExpr(space, op->a, false, false);
  5. support for %

Please separate those changes into independent commits with proper messages. In particular atm it is difficult to see the difference in the translateParam function between changes related to early exit and changes related to new behavior.

Thanks!

Check(
R"(
def local_sparse_convolution(float(N, C, H, W) I, float(O, KC, KH, KW) W1) -> (O1) {
O1(n, o, h, w) +=! I(n, kc % c, h + kh, w + kw) * W1(o, kc, kh, kw) where c in 0:C
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This cannot be correct, it will introduce kc % 0 operations.

tc::CudaMappingOptions::makeConvolutionCudaMappingOptions(),
inputs,
outputs);
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add an actual check function (even if you have to write it using plain C loops).

}
TreeRef c(int kind, const SourceRange& range, TreeList&& trees) {

TreeRef createCompound(int kind, const SourceRange& range, TreeList&& trees) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We have been using makeXYZ syntax throughout the codebase for functions that return an owning pointer, let's try to be consistent.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it was named create... because that's how the functions are named in lexer/sema/parser codebade to create a new node like: https://github.com/facebookresearch/TensorComprehensions/blob/master/tc/lang/tree_views.h#L204. The coding conventions recommend to have consistency locally within lexer/sema/parser (for example) code base here https://github.com/facebookresearch/TensorComprehensions/blob/master/CodingConventions.md#naming

if the naming should be make..., 1. can you kindly create a rule in CodingConventions.md so that people can follow that? 2. it would be also nice to define when should global/local rules be followed? thanks :)

Copy link
Contributor

@nicolasvasilache nicolasvasilache May 1, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sounds good, local trumps global :)

tc/lang/sema.h Outdated
}
TreeRef s(const std::string& s) {

TreeRef createString(const std::string& s) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We have been using makeXYZ syntax throughout the codebase for functions that return an owning pointer, let's try to be consistent.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[copy-paste from above]:

it was named create... because that's how the functions are named in lexer/sema/parser codebade to create a new node like: https://github.com/facebookresearch/TensorComprehensions/blob/master/tc/lang/tree_views.h#L204. The coding conventions recommend to have consistency locally within lexer/sema/parser (for example) code base here https://github.com/facebookresearch/TensorComprehensions/blob/master/CodingConventions.md#naming

if the naming should be make..., 1. can you kindly create a rule in CodingConventions.md so that people can follow that? 2. it would be also nice to define when should global/local rules be followed? thanks :)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sounds good, local trumps global :)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

createString doesn't appear to be used anywhere.
It may be better to simply remove it (in a separate commit).

@prigoyal prigoyal dismissed nicolasvasilache’s stale review May 1, 2018 20:31

comments have been addressed. Requesting again. Thanks.

if (it != params->end()) {
p = it->second;
} else {
CHECK(d_->kind() == lang::TK_CONST);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This hoisting looks like a behavior change to me, could you please explain why it is necessary or correlated with this commit message?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thanks for the review. I am not sure I understand. There is no code change to this section except removing unnecessary else after line 74.

line 88 - 98 in the green section matches line 85 - 95 in red section. I think that github alignment for code changes is not exactly good and hence causing confusion.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

diff computation alogrithm is not perfect; reviewing changes per-commit helps

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah yes, you're right, I see there are 2 columns of numbers. I learned something today! Sorry for the noise here

@nicolasvasilache
Copy link
Contributor

nicolasvasilache commented May 1, 2018

So it seems the unit test change requested was addressed by simply deleting the unit test.
As it stands, this PR adds % to parsing and checks that the operation is indeed performed on an int32 value which is good.

However as far as I see, it does not address #290 which requires support for modulus array indexing. So I would remove the halide -> isl part from your PR title until this is addressed and a proper test is added.

Could you elaborate on what happens if you try to compile a TC with a modulus array indexing? I assume the parsing now passes but I would expect some undefined behavior when reaching ISL.

Does the TC mapper complain in an actionable way, just segfault/crash, produce some wrong code or something else?

)TC";
// This triggers tc2halide conversion and should not throw.
auto scop = Prepare(tc);
auto reads = scop->reads;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hey @ftynse , I have extracted out the reads here but I don't know how to get the isl stride information. I tried to search in the codebase but couldn't find much help. Can you give some pointers? thanks.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, you probably want to search in the third-party/islpp as well :) There is a greppable documentation file in doc/.

Something along the lines of

for (auto r : scop->reads.range()) {
  if (r.get_tuple_name() != std::string("B")) { // skip irrelevant reads, if any
    continue;
  }
  EXPECT_EQ(r.get_stride(), putExpectedStrideValueHere);
  // EXPECT_TRUE(r.plain_is_universe()); // for the the set being universe
}

should work

Copy link
Contributor Author

@prigoyal prigoyal May 2, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thanks. I tried the above snippet but getting compilation failure on for (auto r : scop->reads.range()) it seems like it's not iterable. The error looks like

error: no matching function for call to ‘begin(isl::union_set&)’

is the https://github.com/simbuerg/isl/blob/master/doc/user.pod correct file to consult?

Copy link
Contributor

@ftynse ftynse May 2, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, my bad, try scop->reads.range().get_set_list() instead.

This one https://github.com/nicolasvasilache/isl/blob/ntv_dev/doc/user.pod comes from what we submodule (our copy of isl is ahead even of isl's master, and the link you posted is not master)

@prigoyal prigoyal dismissed skimo-openhub’s stale review May 2, 2018 20:44

addressed all feedback so far. requesting guidance on unit test added in test_cuda_mapper

zdevito
zdevito previously approved these changes May 2, 2018
Copy link
Contributor

@zdevito zdevito left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Frontend changes look good to me. I didn't catch where in halide2isl that % was added. Did it previously exist?

@prigoyal
Copy link
Contributor Author

prigoyal commented May 2, 2018

the % operator existed in halide2isl but it was segfaulting. 52989ea commit fixed that :)

@skimo-openhub
Copy link
Contributor

skimo-openhub commented May 3, 2018 via email

@prigoyal prigoyal force-pushed the add-operators branch 2 times, most recently from 0960f03 to 022c62a Compare May 3, 2018 14:12
if (r.get_tuple_name() != std::string("a")) {
continue;
}
// EXPECT_EQ(r.get_stride(?), ?);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not quite sure what should be the pos for get_stride().

Copy link
Contributor

@ftynse ftynse May 3, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

index of the dimension in the set (position of the subscript that should involve the stride), 0 in this case I believe

@prigoyal prigoyal force-pushed the add-operators branch 2 times, most recently from 8e390f3 to 34bd0b1 Compare May 3, 2018 17:41
if (r.get_tuple_name() != std::string("a")) {
continue;
}
// EXPECT_EQ(r.get_stride(0), ?);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this test is still commented out...

)TC";
// This triggers tc2halide conversion and should not throw.
auto scop = Prepare(tc);
for (auto r : scop->reads.range().get_set_list()) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, I misled you here; taking the range of the access relation here will actually drop the stride because the stride is on the input dimension. Instead, let's use wrapping. It is a transformation that reshapes the space from a relation ([i]->a[a0]) to a set wrapping a relation ([[i]->a[a0]], note the extra surrounding [] that mean it's a set now). In that set, we can take the stride an compare it with the expected value. However, we cannot directly check the ex-range's name, so we need to unwrap the set and take its range before using get_tuple_name().

Now that you have an idea of how isl is structured, you should be able to find the (un)wrapping functions and use them properly :)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thanks @ftynse . can you provide guidance on skimo's comment ? I am not sure myself which is the right approach. Thanks a lot :)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's a matter of preference, I think. @skimo-openhub suggests to construct a new relation from its textual description, basically isl::map(ctx, "<paste here the textual representation of the map litearlly>") (taking ctx from whatever isl object available around) and comparing that to the result. It would work, but I prefer testing only the relevant properties of the relation, hence the stride check. Checking only relevant properties removes the need to modify the test when irrelevant properties change. I will accept both ways of checking.

More specifically, current access relations do not involve bounds imposed by tensor sizes. I'm quite annoyed at this during memory promotion and was planning to add those bounds in access relation construction. That would change these relations, requiring to update this test that is affected only indirectly.

@skimo-openhub
Copy link
Contributor

skimo-openhub commented May 7, 2018 via email

@skimo-openhub
Copy link
Contributor

skimo-openhub commented May 9, 2018 via email

@ftynse
Copy link
Contributor

ftynse commented Jun 6, 2018

rebased and revived

ftynse
ftynse previously approved these changes Jun 7, 2018
prigoyal added 6 commits June 8, 2018 07:57
the 'if' condition is checked and 'return' happens if the condition is
met. Using 'else' is not needed
proper variable naming and whitelining between functions
change makeIslAffBoundsFromExpr(..., e, ...) to
makeIslAffBoundsFromExpr(..., op->a, ...) to avoid infinite recursion
leading to segfault
support % operator: propagate it from parser to halide to isl and add
unit tests
dead function and not used anywhere so cleaning it up
@nicolasvasilache nicolasvasilache merged commit 702d7f5 into master Jun 8, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

The % operator does not seem to work

7 participants